目录置顶:
- 关于项目--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(1)
- 架构搭建--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(2)
- WCF服务端具体实现---------基于DDD领域驱动设计的WCF+EF+WPF分层框架(3)
- WCF客户端配置以及代理-----基于DDD领域驱动设计的WCF+EF+WPF分层框架(4)
- Domain具体实现------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(5)
- WPF的UI层-----------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(6)
- 组织架构--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(7)
- 即时通讯--------------------基于DDD领域驱动设计的WCF+EF+WPF分层框架(8)
- 我的网站
架构搭建
架构是基于DDD领域驱动模型的,DDD的思想我就不介绍了,园里有挺多的介绍了,另外在下面的架构图里面也会有体现,架构图本应该用UML图展示一下的,我觉得麻烦,我直接把我的项目截一个图介绍一下:
项目我分了四个解决方案文件夹:ACS.OA.Common、ACS.OA.UIClient、ACS.OA.WCFServer、ACS.OA.WebSite(这个本次博客不打算介绍,直接忽略)。
ACS.OA.Common
这个文件夹里面我建了Base、Global、Model、WCFContract四个项目,这四个项目大家看名称就知道作用了。
Base的内部结构我也截一张图吧
这个重点在CS文件夹里面,Cache缓存类,Communication通讯类,CustomException 异常处理类,Lib帮助集合(比较多,我把截图放旁边了),Log日志类(Log4Net)
我重点讲一下通讯类Pactet数据包
1 /// <summary> 2 /// 打包、解包 3 /// </summary> 4 /// <typeparam name="T">数据类型 object</typeparam> 5 public abstract class Packet<T> 6 { 7 /// <summary> 8 /// 打包数据 9 /// </summary> 10 /// <param name="t"></param> 11 /// <param name="strKey">加密Key</param> 12 /// <returns></returns> 13 public static byte[] PacketData(T t, string strKey) 14 { 15 var jSetting = new JsonSerializerSettings(); 16 jSetting.NullValueHandling = NullValueHandling.Ignore; //忽略为NULL的值 17 18 byte[] bytContent; 19 string strJson = JsonConvert.SerializeObject(t, jSetting); //T转Json 20 strJson = DESHelper.Encrypt(strJson, strKey); //加密 21 bytContent = SerializeHelper.Serialize(strJson); //压缩转byte[] 22 23 Init(bytContent); 24 return bytContent; 25 } 26 27 /// <summary> 28 /// 解包数据 29 /// </summary> 30 /// <param name="bytContent"></param> 31 /// <param name="strKey">解密Key</param> 32 /// <returns></returns> 33 public static T DePacketData(byte[] bytContent, string strKey) 34 { 35 var jSetting = new JsonSerializerSettings(); 36 jSetting.NullValueHandling = NullValueHandling.Ignore; //忽略为NULL的值 37 38 T t; 39 string strJson = SerializeHelper.Deserialize(bytContent).ToString(); //解压缩转string 40 strJson = DESHelper.Decrypt(strJson, strKey); //解密 41 t = (T)JsonConvert.DeserializeObject(strJson, typeof(T), jSetting); //Json转T 42 return t; 43 } 44 45 }
里面的DESHelper的加密解密方法用的是TripleDESCryptoServiceProvider,需要的话自己引用一下就可以自己写了,这个实例比较多,我就不贴代码了。
我说一下我为何这么做的原因
打包步骤:
我先把实体类转为Json字符串目的为统一传输规则,这样做不用考虑发送方和接收方是什么语言开发的,接收方收到json解析就行了。
我又把Json串加密,这是为了安全考虑,数据传输用明文不安全吧。
转byte[] 压缩一下,文件会小很多,考虑的是传输效率。
解包步骤 就是打包的逆向了,不解释了啊。
Lib帮助类集合里面,我讲一下WCFHandler类,先贴代码
1 public class WCFHandler 2 { 3 public static T CreateHttpServiceClient<T>(string webAddress, string serviceName) 4 { 5 T t = default(T); 6 object obj = Activator.CreateInstance(typeof(T), new object[] 7 { 8 GetHttpBinding(), 9 GetEndpoint(webAddress, serviceName) 10 }); 11 t = (T)(obj); 12 return t; 13 } 14 15 public static T CreateTcpServiceClient<T>(string webAddress, string serviceName, bool isStream=false) 16 { 17 T t = default(T); 18 object obj; 19 if (isStream) //流传输 20 { 21 obj = Activator.CreateInstance(typeof(T), new object[] 22 { 23 GetFileTcpBinding(), 24 GetEndpoint(webAddress, serviceName) 25 }); 26 } 27 else //缓存传输 28 { 29 obj = Activator.CreateInstance(typeof(T), new object[] 30 { 31 GetTcpBinding(), 32 GetEndpoint(webAddress, serviceName) 33 }); 34 } 35 t = (T)(obj); 36 return t; 37 } 38 39 public static NetTcpBinding GetTcpBinding() 40 { 41 return new NetTcpBinding 42 { 43 MaxBufferPoolSize = 2147483646L, 44 MaxReceivedMessageSize = 2147483646L, 45 CloseTimeout = new TimeSpan(0, 0, 10), 46 OpenTimeout = new TimeSpan(0, 0, 10), 47 ReceiveTimeout = TimeSpan.MaxValue, 48 SendTimeout = new TimeSpan(0, 20, 0), 49 ReaderQuotas = 50 { 51 MaxDepth=64, 52 MaxStringContentLength=2147483646, 53 MaxArrayLength=2147483646, 54 MaxBytesPerRead=2147483646, 55 MaxNameTableCharCount=2147483646 56 }, 57 ReliableSession = 58 { 59 Enabled = true, 60 Ordered = true, 61 InactivityTimeout = new TimeSpan(0, 0, 10) 62 }, 63 Security = 64 { 65 Mode=SecurityMode.None, 66 Message = 67 { 68 ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows 69 }, 70 Transport = 71 { 72 ClientCredentialType = TcpClientCredentialType.Windows 73 } 74 }, 75 76 }; 77 } 78 79 80 /// <summary> 81 /// TCP大文件断点续传 82 /// </summary> 83 /// <returns></returns> 84 public static NetTcpBinding GetFileTcpBinding() 85 { 86 return new NetTcpBinding 87 { 88 MaxBufferPoolSize = 2147483646L, 89 MaxReceivedMessageSize = 2147483646L, 90 CloseTimeout = new TimeSpan(0, 0, 10), 91 OpenTimeout = new TimeSpan(0, 0, 10), 92 ReceiveTimeout = TimeSpan.MaxValue, 93 SendTimeout = new TimeSpan(0, 20, 0), 94 TransferMode=TransferMode.Streamed, 95 ReaderQuotas = 96 { 97 MaxDepth=64, 98 MaxStringContentLength=2147483646, 99 MaxArrayLength=2147483646, 100 MaxBytesPerRead=2147483646, 101 MaxNameTableCharCount=2147483646 102 }, 103 //ReliableSession = 104 //{ 105 // Enabled = true, 106 // Ordered = true, 107 // InactivityTimeout = new TimeSpan(1, 0, 0) 108 //}, 109 //Security = 110 //{ 111 // Mode=SecurityMode.None, 112 // Message = 113 // { 114 // ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows 115 // }, 116 // Transport = 117 // { 118 // ClientCredentialType = TcpClientCredentialType.Windows 119 // } 120 //}, 121 122 }; 123 } 124 125 public static WSHttpBinding GetHttpBinding() 126 { 127 return new WSHttpBinding 128 { 129 MaxBufferPoolSize = 2147483646L, 130 MaxReceivedMessageSize = 2147483646L, 131 CloseTimeout = new TimeSpan(0, 0, 10), 132 OpenTimeout = new TimeSpan(0, 0, 10), 133 ReceiveTimeout = new TimeSpan(0, 20, 0), 134 SendTimeout = new TimeSpan(0, 20, 0), 135 ReliableSession = 136 { 137 Enabled = true, 138 Ordered = true, 139 InactivityTimeout = new TimeSpan(0, 0, 10) 140 }, 141 UseDefaultWebProxy = false, 142 Security = 143 { 144 Mode = SecurityMode.None, 145 Message = 146 { 147 ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows 148 }, 149 Transport = 150 { 151 ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows 152 } 153 }, 154 }; 155 } 156 public static EndpointAddress GetEndpoint(string webAddress, string serviceName) 157 { 158 Uri uri = new Uri(webAddress + "/" + serviceName + ".svc"); 159 return new EndpointAddress(uri, new AddressHeader[0]); 160 } 161 162 public static EndpointAddress GetIMEndpoint(string webAddress, string serviceName) 163 { 164 Uri uri = new Uri(webAddress + "/" + serviceName + ".svc"); 165 return new EndpointAddress(uri, new AddressHeader[0]); 166 } 167 }
这个里面是NET.TCP 和WSHttp 的WCF配置,这个类主要用于客户端的 需要传入两个参数“webAddress”,“serviceName”
webaddress我的做法是配置在客户端的App.Config读取。
serviceName我是根据每一个不同方法手工写入的
我再贴一下用法实例
1 /// <summary> 2 /// 艾克仕网络云OA企业名片 3 /// </summary> 4 public FirmCardPacket GetFirmCard(FirmCardPacket packet) 5 { 6 using (SettingServiceClient client = CreateTcpServiceClient<SettingServiceClient>(Caches.GolbalCache.WCFAddressCache, "MainClient/SettingService")) 7 { 8 client.Open(); 9 byte[] bt = null; 10 bt = Packet<FirmCardPacket>.PacketData(packet, strKey); 11 bt = client.GetFirmCard(bt); 12 packet = Packet<FirmCardPacket>.DePacketData(bt, strKey); 13 client.Close(); 14 } 15 return packet; 16 }
这个现在就展示一下用法,到我把WCF代理讲完,在UI里面我详细讲。
Base主要的是这些了,下面讲Global
先贴图
Global项目就是它的字面意思,全局的信息或者配置就放在这里。这个不用多讲了,大家如果使用我这套架构的话,如何划分全局还得靠自己,每个项目一般都不一样的。
下面是Model
Model我的文件夹分类规则是一个客户端项目对应一个Model文件夹,多个客户端公用的在Common文件夹,Base文件夹是基类举一个例子说明基类的用法
WPF需要实时刷新界面数据的话,我的实体如IMinfo就会继承Notify。IMpacket是具体的数据包需要继承PacketModel。
PacketModel里面是一些公共属性,LoginId,FirmId,ResultCode,ResultMsg 这些信息。
我贴一下Notify的代码
1 public abstract class Notify : INotifyPropertyChanged 2 { 3 public event PropertyChangedEventHandler PropertyChanged; 4 5 protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 6 { 7 if (object.Equals(storage, value)) return false; 8 9 storage = value; 10 this.OnPropertyChanged(propertyName); 11 12 return true; 13 } 14 15 protected void OnPropertyChanged(string propertyName) 16 { 17 var eventHandler = this.PropertyChanged; 18 if (eventHandler != null) 19 { 20 eventHandler(this, new PropertyChangedEventArgs(propertyName)); 21 } 22 } 23 }
这个代码不解释了,做WPF的都知道。
ACS.OA.WCFContract
WCF契约,我为何要写在这里呢?是因为我是使用SvcUtil生成的WCF代理类,这个代理类和WCF服务库都公用这个契约,代理类和服务库的代码就会很简洁了,而且可以保证代理类和服务库的方法一致。
契约不用贴图,WCF有明确规定的,等到即时通讯需要双工的地方我会贴特定的契约接口。
到此,ACS.OA.Common解决方案文件夹讲完,下面讲ACS.OA.WCFServer,不要走开,精彩还在后面
ACS.OA.WCFServer
<p><span style="font-size: 16px;"><strong><span style="color: #ff0000;"><a href="http://www.cnblogs.com/acssoft/p/5370286.html"><span style="color: #ff0000;">写在最前面:转载请注明出处</span></a></span></strong></span></p>