目录置顶:
- 关于项目--------------------基于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数据包
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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类,先贴代码
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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,不要走开,精彩还在后面
DDD WCF 服务层 ACS.OA.WCFServer
这个文件夹里面我建了ACS.OA.Application、Domain、Repositories、WCFService、WCFServiceLib 五个项目。
这部分我将DDD和WCF整合统一放在WCFServer解决方案文件夹下。
我讲一下数据处理过程:UI层通过WCF服务通信到WCFService由WCFServiceLib-->Application-->Domain-->Repositories。
WCFService 项目里面只有SVC和Web.Config
契约如前面写的我已经单独新建项目放在ACS.OA.Common文件夹中,这里引用就可以了。svc.cs文件我也删掉放在WCFServiceLib中,这里也是引用。
我贴一下Web.config配置给大家做一个参考
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 <system.serviceModel> 2 <bindings> 3 <netTcpBinding> 4 <binding name="OATcpBinding" closeTimeout="00:00:10" openTimeout="00:00:10" 5 receiveTimeout="00:30:00" sendTimeout="00:30:00" transactionFlow="true" 6 transferMode="Buffered" maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646" 7 portSharingEnabled="true"> 8 <readerQuotas maxDepth="64" maxStringContentLength="2147483646" 9 maxArrayLength="2147483646" maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" /> 10 <reliableSession ordered="true" inactivityTimeout="00:00:10" 11 enabled="true" /> 12 <security mode="None" /> 13 </binding> 14 </netTcpBinding> 15 16 <wsHttpBinding> 17 <binding name="OAHttpBinding" closeTimeout="00:05:00" openTimeout="00:05:00" 18 receiveTimeout="00:05:00" sendTimeout="00:05:00" transactionFlow="true" 19 maxBufferPoolSize="2147483646" maxReceivedMessageSize="2147483646" 20 messageEncoding="Text"> 21 <readerQuotas maxDepth="64" maxStringContentLength="2147483646" 22 maxArrayLength="2147483646" maxBytesPerRead="2147483646" maxNameTableCharCount="2147483646" /> 23 <reliableSession ordered="true" inactivityTimeout="01:00:00" 24 enabled="true" /> 25 <security mode="None" /> 26 </binding> 27 </wsHttpBinding> 28 </bindings> 29 <services> 30 31 <!--即时通讯Begin--> 32 <service behaviorConfiguration="OATcpBehavior" name="ACS.OA.WCFServiceLib.MainClient.IMService"> 33 <endpoint binding="netTcpBinding" bindingConfiguration="OATcpBinding" name="IMService" contract="ACS.OA.WCFContract.MainClient.IIMService"> 34 <identity> 35 <dns value="OAMainClient"/> 36 </identity> 37 </endpoint> 38 <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/> 39 </service> 40 <!--即时通讯End--> 41 42 <!--文件断点续传Begin--> 43 <service behaviorConfiguration="OATcpBehavior" name="ACS.OA.WCFServiceLib.Common.ACSFileService"> 44 <endpoint binding="netTcpBinding" bindingConfiguration="OATcpBinding" name="ACSFileService" contract="ACS.OA.WCFContract.Common.IACSFileService"> 45 <identity> 46 <dns value="OAMainClient"/> 47 </identity> 48 </endpoint> 49 <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/> 50 </service> 51 <!--文件断点续传End--> 52 53 </services> 54 <behaviors> 55 <serviceBehaviors> 56 <behavior> 57 <!-- 为避免泄漏元数据信息,请在部署前将以下值设置为 false --> 58 <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/> 59 <!-- 要接收故障异常详细信息以进行调试,请将以下值设置为 true。在部署前设置为 false 以避免泄漏异常信息 --> 60 <serviceDebug includeExceptionDetailInFaults="false"/> 61 </behavior> 62 <behavior name="OATcpBehavior"> 63 <serviceMetadata httpGetEnabled="false"/> 64 <serviceDebug includeExceptionDetailInFaults="true"/> 65 <dataContractSerializer maxItemsInObjectGraph="2147483646"/> 66 <serviceTimeouts transactionTimeout="00:05:00"/> 67 <!--会话最大数量--> 68 <serviceThrottling maxConcurrentSessions="10000" maxConcurrentCalls="10000" maxConcurrentInstances="10000"/> 69 </behavior> 70 71 <behavior name="OAHttpBehavior"> 72 <serviceMetadata httpGetEnabled="true"/> 73 <serviceDebug includeExceptionDetailInFaults="true"/> 74 <dataContractSerializer maxItemsInObjectGraph="2147483646"/> 75 <serviceTimeouts transactionTimeout="00:05:00"/> 76 <!--会话最大数量--> 77 <serviceThrottling maxConcurrentSessions="10000" maxConcurrentCalls="10000" maxConcurrentInstances="10000"/> 78 </behavior> 79 </serviceBehaviors> 80 <endpointBehaviors> 81 <behavior name="DuplexendpointBehaviors"> 82 <dataContractSerializer maxItemsInObjectGraph="2147483646"/> 83 </behavior> 84 </endpointBehaviors> 85 </behaviors> 86 <protocolMapping> 87 <add binding="basicHttpsBinding" scheme="https"/> 88 </protocolMapping> 89 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/> 90 </system.serviceModel>
ACS.OA.WCFServiceLib
这里我贴一下WCFServiceLibBase和其中一个服务的代码
public class WCFServiceLibBase { public string HandleException(Exception ex, bool isThrowOut = false) { var myExceptionManager = ExceptionManager.GetInstance(); string returnValue = myExceptionManager.HandleException(ex, this.GetType().Assembly.GetName().Name, this.GetType().FullName, isThrowOut); return returnValue; } public byte[] ErrorLogRecode(Exception ex,string funcName=null) { HandleException(ex); LoginPacket backPacket = new LoginPacket(); backPacket.ResultMsg = "操作失败"; LogHelper.WriteErrorLog(this.GetType().FullName, "ErrorLogRecode", ex); bool recordResult= LogRecordServiceImpl.Instance.Add(ex.Message, funcName); if (!recordResult) { backPacket.ResultMsg = "网络异常,请稍后再试"; } return Packet<LoginPacket>.PacketData(backPacket, SystemConfig.Packet_Key); } }
/// <summary> /// 艾克仕网络云OA企业名片 /// </summary> public byte[] GetFirmCard(byte[] bytData) { try { return SettingServiceImpl.Instance.GetFirmCard(bytData); } catch (Exception ex) { return ErrorLogRecode(ex); } }
这段代码大家可以看到其实真正的数据处理还不是这里,这里仅仅进行了一个异常处理,对外的接口如有异常会返回一个通用的错误信息。
SettingServiceImpl.Instance.GetFirmCard(bytData);从这行代码就知道我们需要去Application看了。
ACS.OA.Application
1 public class SettingServiceImpl 2 { 3 #region <<Singleton>> 4 private static object obj_lock = new object(); 5 private static SettingServiceImpl _instance = default(SettingServiceImpl); 6 7 public static SettingServiceImpl Instance 8 { 9 get 10 { 11 if (_instance == null) 12 { 13 lock (obj_lock) 14 { 15 if (_instance == null) 16 { 17 _instance = Activator.CreateInstance<SettingServiceImpl>(); 18 } 19 } 20 } 21 return _instance; 22 } 23 } 24 25 #endregion 26 27 private FirmCardDbContext firmCardDbContext; 28 private IFirmCardRepository firmCardRepository; 29 30 /// <summary> 31 /// 艾克仕网络云OA企业名片列表 32 /// </summary> 33 public byte[] GetFirmCard(byte[] bytData) 34 { 35 FirmCardPacket backPacket = new FirmCardPacket(); 36 //byte转化实体 37 FirmCardPacket packet = Packet<FirmCardPacket>.DePacketData(bytData, SystemConfig.Packet_Key); 38 string connStr = CommonServiceImpl.Instance.GetServerSourceByFirmRegNo(packet.FirmRegNo); 39 if (string.IsNullOrEmpty(connStr)) 40 { 41 packet.ResultMsg = "获取连接参数异常,请登陆后重试"; 42 return Packet<FirmCardPacket>.PacketData(backPacket, SystemConfig.Packet_Key); 43 } 44 firmCardDbContext = new FirmCardDbContext(connStr); 45 46 using (IRepositoryContext recontext = new EFRepositoryContext(firmCardDbContext)) 47 { 48 firmCardRepository = new FirmCardRepository(recontext); 49 50 List<FirmCardEntity> ents = new List<FirmCardEntity>(firmCardRepository.GetAll()); 51 if (ents != null && ents.Count > 0) 52 { 53 backPacket.FirmCardList = new FirmCardEntityDTO().ConvertFirmCardToInfoByEntityList(ents); 54 } 55 } 56 backPacket.ResultCode = ResultCodeType.CODE_SUCCESS; 57 return Packet<FirmCardPacket>.PacketData(backPacket, SystemConfig.Packet_Key); 58 } 59 } 60 }
熟悉设计模式的都知道Instance 是单例模式,这里把Application设计成单例模式我的理由是这里是服务端,这里客户端访问的频率会很高,这个类在加载之时就实例化存于内存之中,不用每次访问都去实例化然后再垃圾回收再实例再回收会增加服务器压力。所以用的单例模式。
private FirmCardDbContext firmCardDbContext;
private IFirmCardRepository firmCardRepository;
这两个是先声明数据上下文和仓储库。在每一次请求都去实例化,请求结束立即释放。为了防止数据库被独占。里面具体的方法实现后续会做详细介绍。
ACS.OA.Domain 和 ACS.OA.Repositories
在这里我不想过多介绍Domain和Repositories,我就截图看一下结构。在后面具体实例中从头走到尾。
WPF UI层 ACS.OA.UIClient
我这个艾克仕网络云OA的UI层有5个项目 ACS.OA.WCFClient、ACS.ClientBase、ACS.Launcher、ACS.UpdateClient、ACS.OA.MainClient
ACS.Launcher、ACS.UpdateClient 这两个是做检测和更新的,这个不做介绍。
ACS.ClientBase
ClientBase项目我的目的是定义一些公共的类,每一个客户端都公用的。在Base文件夹里面有ModelBase、NotifyBase、ViewModelBase,这三个基类,在MainClient中每一个Model类,Notify类和ViewModel类都会继承相对应的基类。下面我把三个基类的代码贴出来:
public class ModelBase { #region <<WCF Related Methods>> public T CreateTcpServiceClient<T>(string webAddress, string serviceName, bool isStream = false) { return WCFHandler.CreateTcpServiceClient<T>(webAddress, serviceName, isStream); } /// <summary> /// IM即时通讯专用 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="callbackInstance"></param> /// <param name="webAddress"></param> /// <param name="serviceName"></param> /// <returns></returns> public T CreateTcpServiceClient<T>(InstanceContext callbackInstance, string webAddress, string serviceName) { return WCFHandler.CreateTcpServiceClient<T>(callbackInstance, webAddress, serviceName); } #endregion }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 public class NotifyBase : 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 }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 /// <summary> 2 /// ViewModelBase 3 /// </summary> 4 /// <typeparam name="T">Model</typeparam> 5 public abstract class ViewModelBase<T> : BindableBase 6 { 7 private static object obj_lock = new object(); 8 private T _Model = default(T); 9 10 public T Model 11 { 12 get 13 { 14 if (_Model == null) 15 { 16 lock (obj_lock) 17 { 18 if (_Model == null) 19 { 20 _Model = Activator.CreateInstance<T>(); 21 } 22 } 23 } 24 return _Model; 25 26 } 27 } 28 29 #region 异常 30 public string HandleException(Exception ex, bool isThrowOut=false) 31 { 32 string returnValue = ""; 33 try 34 { 35 LogHelper.WriteErrorLog(this.GetType().FullName, "ErrorLogRecode", ex); 36 var myExceptionManager = ExceptionManager.GetInstance(); 37 //if (GlobalVariables.HxIsSendExceptionMail) 38 //{ 39 // handlerTypeList.Add(HandlerType.Mail); 40 //} 41 returnValue = myExceptionManager.HandleException(ex, this.GetType().Assembly.GetName().Name, this.GetType().FullName, isThrowOut); 42 } 43 catch 44 { } 45 return returnValue; 46 } 47 #endregion 48 49 #region 消息 50 /// <summary> 51 /// 自定义消息窗口 52 /// </summary> 53 /// <param name="strMsg">消息</param> 54 /// <param name="isAutoClose">是否自动关闭</param> 55 public void MsgBoxShow(Enum.MsgType msgtype, string strMsg,bool isAutoClose=true) 56 { 57 Application.Current.Dispatcher.Invoke(new Action(() => 58 { 59 MsgBoxShowWindow win = new MsgBoxShowWindow(msgtype, strMsg, isAutoClose); 60 win.Owner = Application.Current.MainWindow; 61 win.Show(); 62 })); 63 } 64 65 /// <summary> 66 /// 自定义消息窗口 67 /// </summary> 68 /// <param name="ex">错误消息</param> 69 /// <param name="isAutoClose">是否自动关闭</param> 70 public void MsgBoxShow(Exception ex, bool isAutoClose=true) 71 { 72 Application.Current.Dispatcher.Invoke(new Action(() => 73 { 74 MsgBoxShowWindow win = new MsgBoxShowWindow(ex, isAutoClose); 75 win.Owner = Application.Current.MainWindow; 76 win.Show(); 77 })); 78 } 79 80 /// <summary> 81 /// 自定义消息窗口 82 /// </summary> 83 /// <param name="strMsg">消息</param> 84 /// <param name="isAutoClose">是否自动关闭</param> 85 public void MsgBoxShowDialog(Enum.MsgType msgtype, string strMsg, bool isAutoClose = false) 86 { 87 Application.Current.Dispatcher.Invoke(new Action(() => 88 { 89 MsgBoxShowWindow win = new MsgBoxShowWindow(msgtype, strMsg, isAutoClose); 90 win.Owner = Application.Current.MainWindow; 91 win.ShowDialog(); 92 })); 93 } 94 95 96 /// <summary> 97 /// 自定义消息窗口 98 /// </summary> 99 /// <param name="ex">错误消息</param> 100 /// <param name="isAutoClose">是否自动关闭</param> 101 public void MsgBoxShowDialog(Exception ex, bool isAutoClose = false) 102 { 103 Application.Current.Dispatcher.Invoke(new Action(() => 104 { 105 MsgBoxShowWindow win = new MsgBoxShowWindow(ex, isAutoClose); 106 win.Owner = Application.Current.MainWindow; 107 win.ShowDialog(); 108 })); 109 } 110 111 #endregion 112 113 114 #region 日志 115 116 #endregion 117 }
具体的使用方法在后续介绍UI层会做展示
ACS.OA.WCFClient 是用SvcUtil工具生成的代理类。在这里我想提一点建议
我建议大家在搭建稍微大型一点的项目的时候如果使用WCF尽量使用代理类,不要去右键添加服务引用 添加的服务引用不管是管理还是发布都挺麻烦的。
行了,架构搭建就讲到这里,请关注后续博客。也请拍砖,轻点拍