在博客园看到这篇文章,觉得还不错,发出来跟大家一起分享: 好多技术人员都有一个通病,不关注用户的需求,产品的可用性,只看使用的技术的新不新,潮不潮,这就是所谓的技术发烧友。
这段时间,断断续续的开发一个WPF的软件,也拿出来Show一下。要不放在硬盘里就发霉了。 热点一:SOA的分布式理念 现在的开发理念,不管是企业级的ERP,还是网站式的应用,都涉及到了SOA的分布式。就拿一个比较典型的网站来说吧,做网站的童鞋们对CAS,SSO这些关键字并不陌生吧,实质就是应用了SOA的理念,把一个网站平台的认证授权单独抽取出来独立成一个系统,其它业务网站的认证授权都以这个为基础,实现了单点的登录,授权。其实在企业级的ERP中,我们也是这么干的,我们把认证授权提取成一个独立的基础平台(这个甚而平台有可能包括:公共基础数据系统,消息系统,工作流,报表)的一个子系统,其它业务系统都围绕着这个基础平台进行开发。如下图所示,把每个业务系统的权限都提取出来,放在基础平台。
因为这是一个基础开发平台,所以必须为别的应用程序留下服务接口,方便的与别的应用程序通信,实现其软件的价值。 热点二:WCF的安全机制(拦截器) 我们把通用的功能都提取出来组成了一个基础开发平台,以后的业务系统都只关心业务固然是好。不过有点乌托邦了。你想想,业务系统与基础系统怎样通信?怎和样保证通信的安全,这个问题曾经困扰了我好久。系统之间的通信,大家不约而同的可能都想到了Web Service或者WCF吧,不过怎样保证Web Service,WCF的安全呢?我还看到过有人用javascript来调用web Service(Web Service做的是CRUD的操作),这是把自己的衣服脱了赤裸裸的让别人来攻击,给我吓出了一身冷汗呀! 那我们怎样来保证Web Service 或者WCF的安全呢?最笨的方法可以为每个WCF或者Web Service定义两个参数(一个用户名,一个加密码的密码), 或者再改进一下定义一个Token的参数(这个方法我这样做过,鄙视自己一下),有的为了代码看起来更优雅一些,在请求服务时把这个Token写在Header里。然后在执行Web Service 或者 WCF的主体之前,根据传过来的参数在数据库里查询一下,判断这个请求是否有权限。不过一些中大型的系统,一般都会有上百甚至几千个Web Service,每个服务都定义一个参数,或者在Header里定义一个键值对,是不是显得代码很臃肿呢?我们针对这样的问题,我们采用WCF特别的机制拦截器。 WCF中的拦截器其实就是AOP的一个实现,当然这个实现我们也可以自己来写。
在调用每个Web Service或者WCF之前,我都加一个Token在Header里,这个动作,我们完全可以公用,就是采用AOP技术。 在公共基础平台接收到请求的时候,同样可以用AOP的技术,判断Token是否有效。具全实现看源码。 热点三:WPF 在bs结构的系统中,HTML5的UI比较有前景,在cs结构的UI中,我想WPF应该有她独特的优势,在这儿我们暂时不讨论平台的好坏,技术优劣。且看我怎样用WPF来实现一个软件的UI。 1)个性化的菜单: 2)炫耀的表单验证 3)自定义,动态生成xaml的模板 4)目前正在用mvvmlight改写原有的代码。 这些亮点都不得不让我用WPF,这是一个技术爱好者的执着追求,是我的理念。 热点四:Code First 我们什么SOA, AOP, IOC等热点技术都用上了,千万不要拉下了ORM,ORM有Ibatis, hibernate,daper等,但是从性能,可使用性上来说各有千秋,没有优劣,但是我感觉在.net平台上,只有entity framework才是真正的可用orm。entity framework有三种模式,mode first, database first, code first。我为什么在这个项目中选择code first是有原因的。因为我定义的实体,既想作为生成数据库的定义,又想作为WCF中数据传输的数据契约。往往单方面从一个技术角度来看并不难,但是技术与技术叠加,就会出现很多稀奇的问题。我上一段代码片段: 其中红色的是WCF数据契约的标识,蓝色的是Code First的数据定义。把这两种定义都集中在Model上,减少了业务层代码,业务层重点放在了处理业务上。 回忆做技术这些年,06年每天盯着“天轰穿”的视频不亦乐乎,估计那是一代程序员的记忆,08年受益于Artech老师的WCF,为现在的 SOA开发打下了基础,后来又涉及到MVC,EXTJS,Telerik,devexpress,工作流,报表,AGILE 等知识,都存储在我的硬盘里,为了与大家一起分享盛宴,全都拿出来倒腾一番。 闲话少说,切入正题。 今天我要说的,对于多数人来说并不陌生。Instant Messenger,简称IM,中文名:即时通信,土鳖叫法:网页聊天程序。不管有着什么样的名字,就是指的我今天要说的东东,首先上个截图让大家欣赏一下,有图有真相。 最初打算把这个软件包装一下,做出一个商品来卖,就连商品的名称都取好了“两只狐狸”,由于自己的懒惰迟迟没有动起来。这个聊天软件很炫吧,支技图片的传输,表情的发送,在线用户的更新等。别看功能炫,核心代码就几十行javascript和一百多行C#代码。大家看到这儿是不是蠢蠢欲动,准备开始模仿了,那我们就来一起行动吧! 热点一:Comet,实现服务器推技术 Comet从字面上理解有慧星之义,有些天文常识的人都知道,慧星有一个尾巴,拖着闪亮的尾巴划空而过,非常的漂亮,听说这个时候许愿很灵验。今个儿说的Comet可不是一颗漂亮的慧星,而是一个Http请求。Comet因为一个尾巴出名,那么这里的Http就应该有一个长长的尾巴,那怎样才能让HTTP有一个长长的尾巴吗?一个正常的HTTP,包括了客户端的请求和服务器端的响应,一般请求时间和响应时间都能在比较短的时间完成,这个时候的时间状态图,就像是一个点。假设客户端发出了一个请求,服务器端在等待别的事件,迟迟不给响应,导致了一个请求时间变长。这时候的时间状态图是不是像一颗慧星,拖的很长很长?所以就给这种特殊的HTTP请求取个别名叫Comet。 上边的步骤是一个循环的过程,往往牵涉的循环的东西就很难理解,如果今天止步于纸上谈兵,就有伤大家的兴致,还得实践一把才行。 .net里,有一个IHttpAsyncHandler的接口,就能实现HTTP的尾巴功能,拿编程的专业术语来说就是Async(异步),具体的异步实现请口味代码。 public class TalkAsyncHandler : IHttpAsyncHandler { private TFTalkRepository talkRepository = default(TFTalkRepository); public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) { talkRepository = new TFTalkRepository(context.Server.MapPath(ConfigurationManager.AppSettings["TwoFoxTalkDBPath"])); //throw new NotImplementedException(); TalkAsynResult talkAsynResult = new TalkAsynResult(context, cb, extraData, talkRepository); String clientUid = context.Request.Params["uid"]; String clientUname = context.Request.Params["uname"]; String talkContent = context.Request.Params["content"]; /*接收客户端的请求,并作相应的处理(聊天) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ if (talkContent.Equals("-1", StringComparison.CurrentCultureIgnoreCase)) { talkAsynResult.OnlineUID = clientUid; talkAsynResult.ONlineUName = clientUname; HttpConnection.TalkConns.Add(talkAsynResult); } else { talkAsynResult.Send(); //否则将遍历所有已缓存的client,并将当前内容输出到客户端 List<TalkAsynResult> sendTalk = new List<TalkAsynResult>(); sendTalk = HttpConnection.TalkConns.ToList(); HttpConnection.TalkConns.Clear(); foreach (TalkAsynResult itemTalk in sendTalk) { //把聊天记录插入到数据库 TFTalkModel talkData = new TFTalkModel(); talkData.TalkID = Guid.NewGuid().ToString(); talkData.FromID = context.Request.Params["uid"]; talkData.ToID = itemTalk.OnlineUID; talkData.TalkContent = talkContent; talkData.FromDate = DateTime.Now; talkData.IsAccept = false; talkData.AcceptDate = DateTime.MinValue; talkRepository.AddTalkRecord(talkData); itemTalk.Send(); } } return talkAsynResult; } public void EndProcessRequest(IAsyncResult result) { //throw new NotImplementedException(); } public bool IsReusable { //get { throw new NotImplementedException(); } get { return true; } } public void ProcessRequest(HttpContext context) { //throw new NotImplementedException(); } } public class TalkAsynResult : IAsyncResult { bool _IsCompleted = false; HttpContext _context; AsyncCallback _cb; object _extraData; TFTalkRepository _talkRepository; public String OnlineUID { get; set; } public String ONlineUName { get; set; } public TalkAsynResult(HttpContext context, AsyncCallback cb, object extraData, TFTalkRepository talkRepository) { _context = context; _cb = cb; _extraData = extraData; _talkRepository = talkRepository; } //在Message类中的添加消息方法中,调用该方法,将消息输入到客户端,从而实现广播的功能 public void Send() { List<TFTalkModel> listTalkRecored = _talkRepository.GetTalkRecored(_context.Request.Params["uid"]); String responseContent = JsonConvert.SerializeObject(listTalkRecored, Formatting.Indented, new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }); _context.Response.Write(responseContent); if (_cb != null) { _cb(this); } _IsCompleted = true; } public object AsyncState { //get { throw new NotImplementedException(); } get { return null; } } public WaitHandle AsyncWaitHandle { //get { throw new NotImplementedException(); } get { return null; } } public bool CompletedSynchronously { //get { throw new NotImplementedException(); } get { return false; } } public bool IsCompleted { //get { throw new NotImplementedException(); } get { return _IsCompleted; } } } 客户端,就想起了大家都会的Jquery了,想想07年的时候非常伤心,当时用XmlhttpRequest来实现异步,记得创建一个XmlhttpRequest对象都得考虑浏览器的兼容。 $(document).ready(function () { $("#btnSend").click(function () { talksend(); }); function talksend() { $.post("../talkasync.asyn", { uid: '<%=CurrnetUserInfo.UID %>', uname: '<%=CurrnetUserInfo.UName %>', content: $("#<%=txtNotice.ClientID %>").val() }); editor.html(""); } var ISoddOrEven = true; function talkwait() { $.post("../talkasync.asyn" , { uid: '<%=CurrnetUserInfo.UID %>', uname: '<%=CurrnetUserInfo.UName %>', content: "-1" } , function (data, status) { var resultData = JSON.parse(data); $(resultData).each(function (itemID, itemData) { var itemResult = '<div class="itemTitle">' + itemData.FromName + ' ' + itemData.FromDate + '</div><div class="itemContent">' + itemData.TalkContent + '</div>'; if (ISoddOrEven) { itemResult = '<div class="divItemResult-odd">' + itemResult + '</div>'; ISoddOrEven = false; } else { itemResult = '<div class="divItemResult-even">' + itemResult + '</div>'; ISoddOrEven = true; } $("#divResult").append(itemResult); }); document.getElementById("divResult").scrollTop = document.getElementById("divResult").scrollHeight; //服务器返回消息,再次立连接 talkwait(); } , "html"); } //初始化聊天连接 talkwait(); }); 道理很哆嗦,代码很简单,有时候用代码来说话,是不是更具有说服力。 热点二:Sqlite的数据存储 第一次接触Sqlite在博客园,如今好久没看到博客园发Sqlite的博客了,今天感觉有义务重提一下Sqlite。Sqlite一般是作为嵌入式开发的数据存储的介质。经常用于手机开发,C++嵌入式开发,桌面应用程序等领域。由于一直没有机会把Sqlite用于商业项目中,但是不能磨灭我对Sqlite的探讨。 Step1:我们先找到Sqlite的官方网站,下载在Windows的.net平台下开发的类库 http://www./download.html Step2:要找一个好用的Sqlite可视化工具确是难事,我在网上随便搜索一把, Sqlite3Explorer,SQLite Database Browser,SQLiteStudio ,SQLiteAdmin,Sqlite.Developer,SQLite Manager,Navicat for SQLite,SQLiteSpy就有这些工具,经过我的实践,我感觉SQLiteStudio比较适合我。首先简单易用,其次是免费的,再次是有中文版,最后官方还保持了更新,你说我们有什么理由不去使用呢? 可以在 http:// 下载SQLiteStudio的最新版,同样无图无真相。 这个界面是不是似成相识呢,好像操作习惯都与微软的Sql Server的客户端好相似。不知道是谁模仿谁,在这里不作深究。 热点三:JSON的转换(时间格式) JSON是一个简单的数据规范描述,就像XML一样,就是用来定义描述数据的格式。在实际项目中,最常用的有以一两种场景: 1:把前端的JSON格式的字符串,提交到服务端。相当于一次可以提交多条记录到服务器端,服务器端得到这个JSON格式的字符串,转换为List<T>。 2:服务器端的List<T>以JOSN字符串的格式一次性把多条数据输出到客户端。 其实这两个过程我们手写代码来实,完全是没问题的,我早期也是这么做的,不过也有现呈的工具,我们就没必要闭门造车了。 去 http://www./js.html 这个网站把json2.js这个文件给下载过来。json2.js这个功能就非常强大了,比如我们前端对josn字符串进行添加一个 json对象,删除一个JOSN对象都比较麻烦,但是在JS中,操作Array是件非常容易的事,我们可以用json2.js把josn格式的字符串与Array数据互相转换后操作Array。 在.net的服务端有我们的强大的 Newtonsoft.Json.dll,但是偏偏又遇上了一个时间格式化JSON字符串的问题,这个问题是一个老问题,在Google上搜一大把相关的资料。因为 Newtonsoft.Json把时间转换成了类似于 Date/232378978 这种的时间格式。解决这个问题有着千奇百怪的方法。网上主流是以js来转换时间格式。 function ChangeDateFormat(cellval) { var date = new Date(parseInt(cellval.replace("/Date(", "").replace(")/", ""), 10)); var month = date.getMonth() + 1 < 10 ? "0" + (date.getMonth() + 1) : date.getMonth() + 1; var currentDate = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); return date.getFullYear() + "-" + month + "-" + currentDate; } 有些人感觉这种JS时间格式法太麻烦,直接把时间格式 DateTime 定义为 String。用String来保存时间格式。 不过我感觉这种两种方法,都不是最佳解决方案。何不尝试用: JsonConvert.SerializeObject(listTalkRecored, Formatting.Indented, new IsoDateTimeConverter() { DateTimeFormat = "yyyy-MM-dd HH:mm:ss" }); 这就把把时间格式化为 “yyyy-MM-dd HH:mm:ss”的JSON字符串了。有细心的朋友估计已经在Comet那段代码里看到了这行代码。我感觉不单独提出来说,很多人都发现不了
|
|