可能是我們共同的強迫症,不要說看到,就算想到太多的try-catch也很難接受。
於是,開始了一些嘗試,這些嘗試都算是思維的鍛煉、場面的見識、經驗的積累。
Version1 —— 原始版本
一開始,在ConcreteService中,擁有了太多的try-catch,而影響碼字的興趣。
代碼1 原始代碼 /// <summary> /// 契約 /// </summary> public interface IUpdateManyManyThingContract { bool UpdateSth1(DataSet ds); bool UpdateSth2(DataSet ds); bool UpdateSth3(DataSet ds); bool UpdateSth4(DataSet ds); //... } /// <summary> /// 服務實現 /// </summary> public class ConcreteService : IUpdateManyManyThingContract { private IDao m_Dao; public bool UpdateSth1(DataSet ds) { try { var dt = ds.First(); if (!dt.HasElements()) return true; foreach (DataRow row in dt.Rows) { //構造 var entity = new Branch(row); m_Dao.SaveOrUpdate(entity); } return true; } catch (Exception ex) { Logger.Log(ex); return false; } } public bool UpdateSth2(DataSet ds) { try { } catch (Exception) { } } public bool UpdateSth3(DataSet ds) { throw new NotImplementedException(); } public bool UpdateSth4(DataSet ds) { throw new NotImplementedException(); } //many update methods,many try-catches... }
如上代碼,UpdateSth函數里面都需要實現一個try-catch,而覺得惡心到自己了。
Version2——(Extract Method)提取方法 + Func
於是,基於自己的積累,開始了重構的第一個版本。
針對這個服務(ConcreteService)的特殊性,定制了一個專門的方法進行控制——TrycatchBlock。
代碼2 提取方法片段
/// <summary> /// 服務實現 /// </summary> public class ConcreteService : IUpdateManyManyThingContract { private IDao m_Dao; public bool UpdateSth1(DataSet ds) { return TrycatchBlock(() => { var dt = ds.First(); if (!dt.HasElements()) return true; foreach (DataRow row in dt.Rows) { //構造 var entity = new Branch(row); m_Dao.SaveOrUpdate(entity); } return true; }); } public bool UpdateSth2(DataSet ds) { return TrycatchBlock(() => { //... return true; //... //return false; }); } public bool UpdateSth3(DataSet ds) { throw new NotImplementedException(); } public bool UpdateSth4(DataSet ds) { throw new NotImplementedException(); } //many update methods,many try-catches... //try-catch控制塊 private bool TrycatchBlock(Func<bool> body) { try { return body(); } catch (Exception ex) { Logger.Log(ex); return false; } } }
是的,這是一次進步,將所有的try-catch的功能職責都集中到了一個函數里面,也方便調試了。
但是,還得每個方法都加上一句:return TrycatchBlock(() => { 。。。 })。
從本質上來說,還是在進行中重復。
Version3——過濾器思想(否決)
經過老大的指點:考慮MVC中的類似FilterAttribute的注解。
思路演進:MVC中,有一個HandErrorAttribute的特性,用於攔截控制器或者動作的異常。。。。。。對,這是個思路,但過了沒多久,我就放棄了。
放棄理由:“Request請求——>路由數據——>ControllerInvoker——>反射調用Controller或Action。”,這里面用了很多元數據(***Descriptor,***Invoker等)手段,實現難度不小。
另外,我需要的是“instance.MethodAction”(對象直接調用方法)的方式,因為是為WCF直接提供服務(WCF會根據配置文件中服務的名稱創建服務),不需要使用反射進行動態調用。
Version4——動態代理
瀏覽網頁的過程中,想起動態代理——Castle Dynamic Proxy,是的,Moq,Spring.net等一系列優秀的框架中引用到了它。
V4.1.使用中間層
想起一個老外曾經說過的一句話“計算機的任何問題,都可以通過一個中間層來解決”,當然,這里的中間層,是一個廣泛和抽象的概念,比如,中間1號調中間2號、中間2號調目標,可能是一個遞歸的結構也說不定。
於是使用interceptor繼續一個版本:
代碼3:中間層——ConcreteServiceProxy;攔截器——ServiceDynamicProxyInterceptor。 /// <summary> /// 服務實現 /// </summary> public class ConcreteService : IUpdateManyManyThingContract { private IDao m_Dao; public bool UpdateSth1(DataSet ds) { var dt = ds.First(); if (!dt.HasElements()) return true; foreach (DataRow row in dt.Rows) { //構造 var entity = new Branch(row); m_Dao.SaveOrUpdate(entity); } return true; } public bool UpdateSth2(DataSet ds) { //... return true; //... //return false; } public bool UpdateSth3(DataSet ds) { throw new NotImplementedException(); } public bool UpdateSth4(DataSet ds) { throw new NotImplementedException(); } //many update methods,many try-catches... } public class ConcreteServiceProxy : IUpdateManyManyThingContract { private ConcreteService m_Service; public ConcreteServiceProxy() { m_Service = ServiceDynamicProxyInterceptor.CreateServiceProxy<ConcreteService>(); } public bool UpdateSth1(DataSet ds) { return m_Service.UpdateSth1(ds); } public bool UpdateSth2(DataSet ds) { return m_Service.UpdateSth2(ds); } public bool UpdateSth3(DataSet ds) { return m_Service.UpdateSth3(ds); } public bool UpdateSth4(DataSet ds) { return m_Service.UpdateSth4(ds); } } public class ServiceDynamicProxyInterceptor : IInterceptor { /// <summary> /// 工廠方法 /// </summary> /// <typeparam name="T">服務類型</typeparam> /// <returns>一個經過代理的服務</returns> public static T CreateServiceProxy<T>() where T : class { ProxyGenerator generator = new ProxyGenerator(); ServiceDynamicProxyInterceptor interceptor = new ServiceDynamicProxyInterceptor(); T entity = generator.CreateClassProxy<T>(interceptor); return entity; } public void Intercept(IInvocation invocation) { try { invocation.Proceed(); } catch (Exception ex) { Log.Error(ex.Message); invocation.ReturnValue = false; } } }
上述代碼是一目了然,使用m_Service = ServiceDynamicProxyInterceptor.CreateServiceProxy<ConcreteService>();就得到一個代理過的對象,也就能夠進行攔截。
多了一個中間層——ConcreteServiceProxy,層次分明了,但是代碼同樣沒有減少,這個似乎又看起來多次一舉。
況且還要改配置文件,WCF的配置,如下下划線部分。
<service name="MyNameSpace.Service.ConcreteServiceProxy" behaviorConfiguration="WFServiceBehavior">
V4.2.IOC版
到使用中間層為止,我已經是能夠接受的了。但老大覺得還可以再精簡,確實是經驗豐富,又被指點了,然后提點我使用IOC,目標是去除中間層——ConcreteServiceProxy。
思路:
1) 先使用動態代理創建一個被代理過的(Proxied)ConcreteService對象;
2) 將此對象放入IOC中(如Autofac,Unity等);
3) 如果需要使用ConcreteService類型的實例,從IOC中獲取即可。
注:(去除了中間層——ConcreteServiceProxy;同時ConcreteService不用加try-catch;也不用改配置文件了)
代碼4:去除了中間層——ConcreteServiceProxy;同時ConcreteService不用加try-catch;也不用改配置文件了 public class DependencyRegistrar : IDependencyRegistrar { public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder) { var proxiedService = ServiceDynamicProxyInterceptor.CreateServiceProxy<ConcreteService>(); builder.Register(c => proxiedServicec).As<ConcreteService>().InstancePerRequest(); } public int Order { get { return 0; } } }
一次多好的體驗啊!!!
用DP(Dynamic Proxy)完成了攔截;用IOC完成了DI。
寫到這里,問題來了,WCF能夠通過配置文件配置的服務名稱,即MyNameSpace.Service.ConcreteService,自動去IOC中找到被代理的對象嗎?Autofac.WCF能不能幫助它完成呢?
(附)動態代理鏈接:
http://docs.castleproject.org/Tools.DynamicProxy.ashx
http://www.cnblogs.com/daxnet/archive/2011/09/07/2169520.html
http://www.cnblogs.com/RicCC/archive/2010/03/15/castle-dynamic-proxy.html
總結:就先寫到這里,體驗的感覺哪怕就是一點點,也很爽!歡迎拍磚。
Demo鏈接:http://files.cnblogs.com/pengzhen/TryCatchDemo.rar