麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學院 > 開發(fā)設計 > 正文

Asp.Net大型項目實踐(7)-用Unity實現(xiàn)AOP之事務處理+為啥要用AOP(附源碼)

2019-11-17 03:53:09
字體:
來源:轉載
供稿:網友
在目錄中我計劃對權限管理,異常管理,事務管理,日志管理,異常管理等項目中AOP典型應用場景進行詳細的描述,本篇我們用Unity的Interception來實現(xiàn)項目中的事務處理。

為啥要用AOP
由于這是第一篇寫關于AOP的場景,所以我覺得有必要通俗的說明一下在項目中使用AOP的好處。按照弦哥的慣例,關于AOP大套的理論大家自己去google吧,我下面舉一個通俗的例子來說明。比如在項目中我們有一個業(yè)務邏輯的方法:

    public void 我是一個干凈的業(yè)務邏輯方法()        {            我的N行業(yè)務邏輯代碼....            我的N行業(yè)務邏輯代碼....            我的N行業(yè)務邏輯代碼....            我的N行業(yè)務邏輯代碼....        }
我們可以看到他相對來說“很干凈”,只有他本身應該內聚的業(yè)務邏輯。
接下來項目要求這個干凈的業(yè)務邏輯方法 需要支持:攔截異常并向上層重新拋出異常;記錄異常日志;記錄操作日志(如操作人,操作時間,執(zhí)行時間等..);因為里面多次更新數據庫要綁上事務回滾;要進行權限控制。于是乎他變成了下面這樣一個“不干凈”的業(yè)務邏輯方法:

代碼
    public void 我是一個不干凈的業(yè)務邏輯方法()        {            using(我要記錄操作日志)            {                我要判斷權限                try(我要異常處理)                {                    using (我要事務處理)                    {                        我的N行業(yè)務邏輯代碼....                        我的N行業(yè)務邏輯代碼....                        我的N行業(yè)務邏輯代碼....                        我的N行業(yè)務邏輯代碼....                        我的N行業(yè)務邏輯代碼....                    }                }                catch(Exception e)                {                    throw e;                    我要這里記錄異常日志..                }            }        }
我們看到,由于這些非功能性需求所帶來的代碼耦合在了這個方法里,使得他不干凈了,我上面的偽代碼還是默認你有比較好的這些非功能性需求的公共類,如果沒做好估計更亂,相信這種場景大家在開發(fā)中都會經常遇到。業(yè)務代碼和非功能性需求代碼混雜在一起了 影響了可讀性是其次的,更主要的是當業(yè)務改變時你需要去掉一些非功能性需求的時候,那將是你噩夢的開始....如:在系統(tǒng)剛上線實施的時候 我們可能需要對每個方法進行詳細的日志記錄,異常記錄,以便保證上線實施工作的順利進行。但當系統(tǒng)相對穩(wěn)定的時候,可能對部分不是很重要的方法 我們就想取消掉日志記錄以提高系統(tǒng)性能,要是全部取消還好說,改公共類就OK了,要是要求部分取消.....改到你眼花手抽筋!
下面我們來看這個業(yè)務邏輯方法被AOP之后是啥效果:

代碼
    [我要判斷權限]        [我要記錄操作日志]        [我要事務處理]        [我要異常處理]        public void 我是一個被AOP的業(yè)務邏輯方法()        {            我的N行業(yè)務邏輯代碼....            我的N行業(yè)務邏輯代碼....            我的N行業(yè)務邏輯代碼....            我的N行業(yè)務邏輯代碼....        }
我們用Attribute給這個進行了標記,很明顯這個業(yè)務邏輯方法又“干凈”了。
有同學會撅著小嘴說:按你上面那個日志記錄的例子,取消掉部分方法的日志記錄還不是要修改代碼。。。其實我們除了可以通過Attribute方式對方法進行標記,還可以通過xml文件配置進行實現(xiàn)。
看到這里相信你已經了解為什么我們要在項目恰當的場景使用AOP了,下面我們開始實踐如何使用Unity實現(xiàn)事務處理的AOP 。



首先我們創(chuàng)建一個類TransactionInterceptor,我們叫他事務攔截器

代碼
public class TransactionInterceptor : ICallHandler    {        public IsolationLevel Level { get; PRivate set; }                 protected virtual Isession Session        {            get { return SessionBuilder.CreateSession(); }        }        public TransactionInterceptor(IsolationLevel level,int order)        {            this.Level = level;            this.Order = order;        }        #region ICallHandler 成員        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)        {            using (ITransaction ts = Session.BeginTransaction(Level))            {                var result = getNext()(input, getNext);                if (result.Exception == null)                {                    ts.Commit();                }                else                {                    ts.Rollback();                }                return result;            }        }        public int Order { get; set; }        #endregion    }


a.注意必須繼承ICallHandler;
b.Level 允許設置事務隔離級別
c.Session,因為我們用的是NHibernate,所以這里需要把Session帶過來,你看上去是SessionBuilder.CreateSession()去創(chuàng)建了一個新的Session,其實不然,別忘記了我們的Session的生存周期是綁定在一次asp.net MVC的Action上的;
d.Order表示執(zhí)行順序,因為上面我們知道一個方法可能存在多個攔截器,那么你有可能會需要顯示的控制多個攔截器的執(zhí)行順序;
e. using (ITransaction ts = Session.BeginTransaction(Level))這里我們使用NHibernate的事務處理,我看了下NHibernate的源碼,其實還是用的System.Data.IDbTransaction。
f.注意啦!!var result = getNext()(input, getNext);這句話就表示被攔截的方法里的代碼在這句話出現(xiàn)的時候執(zhí)行
g.注意異常的處理,不能用try catch,而是用if (result.Exception == null) 去判斷是否產生異常,這里多謝 Artech同學的提醒;
h.其實大家可以看到這里事務的處理還是比較基礎的,沒有考慮分布式事務,Ado.net與NHibernate混用等情況。沒關系現(xiàn)在先這樣,因為我們可以預見就算將來需要的時候再加也不會對系統(tǒng)帶來任何震蕩:)。


對于事務的處理我希望顯示的用Attribute對方法進行標記,而不是用XML文件配置。所以我們要定義一個TransactionAttribute,這樣帶這個標記的方法就表示需要攔截進行事務處理:

代碼
/// <summary>    /// 事務處理標記    /// </summary>    public class TransactionAttribute : HandlerAttribute    {        public override ICallHandler CreateHandler(Microsoft.Practices.Unity.IUnityContainer container)        {            return new TransactionInterceptor(Level,Order);        }        /// <summary>        /// 事務隔離級別        /// </summary>        public IsolationLevel Level { get; private set; }        /// <summary>        /// 自定義事務隔離級別        /// </summary>        /// <param name="level"></param>        public TransactionAttribute(IsolationLevel level)        {            this.Level = level;        }        /// <summary>        /// 使用數據庫默認隔離級別        /// </summary>        public TransactionAttribute() : this(IsolationLevel.Unspecified) { }    }
a.注意需要繼承HandlerAttribute;
b.重寫了基類的CreateHandler方法 返回我們剛才建立的事務攔截器TransactionInterceptor



特別注意!!對于事務處理的邊界,正確的設計是在項目中的Services層(為什么事務的邊界是在Services層可以自己好好想想,,也可以google下事務邊界的相關知識。有不少人把事務邊界放在Repositorie層那是完全錯誤的!)

接前面幾篇里講的字典功能那個例子,比如我們需要在一個方法里同時插入2個字典項,并且要綁定事務(當然這不可能是實際業(yè)務,只是為了方便代碼舉例),上面說了要放Services層,代碼如下:
Service接口(里面有個方法叫插2次....):

    public interface IDictionaryService    {        //插2次-_-b        void InsertTwice();    }
Service實現(xiàn):  

代碼
public class DictionaryServiceImpl : IDictionaryService    {        private IDictionaryRepository DictionaryRepository;        public DictionaryServiceImpl(IDictionaryRepository DictionaryRepository)        {            this.DictionaryRepository = DictionaryRepository;        }        //上面的代碼是上篇講的依賴注入,別忘記了:)        #region IDictionaryService 成員        public void InsertTwice()        {            var dic1 = new Dictionary();            var dic2 = new Dictionary();            DictionaryRepository.SaveOrUpdate(dic1);            DictionaryRepository.SaveOrUpdate(dic2);        }        #endregion    }
  
通過依賴注入IDictionaryRepository的SaveOrUpdate方法 插了2個數據字典項,但是并沒有綁定事務。



Unity的AOP可以從3種標記的情況攔截:
TransparentProxyInterceptor:直接在類的方法上進行標記,但是這個類必須繼承MarshalByRefObject...這玩意兒我覺得不大好,不建議用
VirtualMethod:直接在類的方法上進行標記,但這個方法必須是虛方法(就是方法要帶virtual關鍵字)
InterfaceInterceptor:在接口的方法上進行標記,這樣繼承這個接口的類里實現(xiàn)這個接口方法的方法就能被攔截(有點拗口...)
在這里我們使用第三種方法InterfaceInterceptor,所以我們把接口的方法打上標記TransactionAttribute:   


public interface IDictionaryService    {        //插2次-_-b        [Transaction]        void InsertTwice();    }




Unity對AOP的實現(xiàn)還是要依賴于UnityContainer,也有兩種實現(xiàn)方式:代碼實現(xiàn)和XML配置實現(xiàn),這里我們使用XML配置實現(xiàn)。
新建一個XML文件unity.aop.infrastructure.config(注意看里面的注釋):   


代碼
<?xml version="1.0" encoding="utf-8" ?><configuration>  <configSections>    <section name="unity"             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration"/>  </configSections>  <unity>    <typeAliases>      <typeAlias alias="InterfaceInterceptor" type="Microsoft.Practices.Unity.InterceptionExtension.InterfaceInterceptor, Microsoft.Practices.Unity.Interception" />    </typeAliases>    <containers>      <container>        <extensions>          <add type="Microsoft.Practices.Unity.InterceptionExtension.Interception, Microsoft.Practices.Unity.Interception"/>        </extensions>        <extensionConfig>          <add name="interception"              type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationElement, Microsoft.Practices.Unity.Interception.Configuration">            <interceptors>              <interceptor type="InterfaceInterceptor">               <!--這里表示需要對IDictionaryService里的方法進行攔截處理,至于攔截里面哪個方法,是根據繼承了HandlerAttribute的標記決定-->                <key type="Demo.HIS.Infrastructure.Core.Services.IDictionaryService, Infrastructure.Core"/>              </interceptor>            </interceptors>          </add>        </extensionConfig>      </container>    </containers>  </unity></configuration>

然后別忘記把IDictionaryService加到上篇中的unity.di.infrastructure.config文件里:   


代碼
<?xml version="1.0" encoding="utf-8" ?><configuration>  <configSections>    <section name="unity"             type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>  </configSections>    <unity>    <typeAliases>      <!--BeginRepository-->      <typeAlias alias="IDictionaryRepository" type="Demo.HIS.Infrastructure.Core.Repositories.IDictionaryRepository, Infrastructure.Core" />      <!--EndRepository-->      <!--BeginService-->      <typeAlias alias="IDictionaryService" type="Demo.HIS.Infrastructure.Core.Services.IDictionaryService, Infrastructure.Core" />      <!--EndService-->      <!--BeginFacade-->      <typeAlias alias="IDictionaryFacade" type="Demo.HIS.Infrastructure.Facade.IDictionaryFacade, Infrastructure.Facade" />      <!--EndFacade-->    </typeAliases>    <containers>      <container>        <types>          <!--BeginRepository-->          <type type="IDictionaryRepository" mapTo="Demo.HIS.Infrastructure.Repositories.DictionaryRepositoryImpl, Infrastructure.Repositories" />          <!--EndRepository-->          <!--BeginService-->          <type type="IDictionaryService" mapTo="Demo.HIS.Infrastructure.Core.Services.Impl.DictionaryServiceImpl, Infrastructure.Core" />          <!--EndService-->          <!--BeginFacade-->          <type type="IDictionaryFacade" mapTo="Demo.HIS.Infrastructure.Facade.DictionaryFacadeImpl, Infrastructure.Facade" />          <!--EndFacade-->        </types>      </container>    </containers>  </unity></configuration>







還記得上篇我們的ContainerFactory類嗎,XML文件unity.aop.infrastructure.config的讀取還是在這里:   

代碼
public static class ContainerFactory    {        public static IUnityContainer GetContainer()        {            IUnityContainer Container = new UnityContainer();            ExeConfigurationFileMap infraFileMap = new ExeConfigurationFileMap();            infraFileMap.ExeConfigFilename = HttpContext.Current.Server.MapPath("~/unity.di.infrastructure.config");            UnityConfigurationSection infraConfig = (UnityConfigurationSection)ConfigurationManager                .OpenMappedExeConfiguration(infraFileMap, ConfigurationUserLevel.None)                .GetSection("unity");            if (infraConfig.Containers.Default != null)            {                infraConfig.Containers.Default.Configure(Container);            }            //上面是上篇講的DI依賴注入XML文件的讀取,下面是本篇講的AOP的XML文件讀取            ExeConfigurationFileMap infraAopFileMap = new ExeConfigurationFileMap();            infraAopFileMap.ExeConfigFilename = HttpContext.Current.Server.MapPath("~/unity.aop.infrastructure.config");            UnityConfigurationSection infraAopConfig = (UnityConfigurationSection)ConfigurationManager                .OpenMappedExeConfiguration(infraAopFileMap, ConfigurationUserLevel.None)                .GetSection("unity");            if (infraAopConfig.Containers.Default != null)            {                infraAopConfig.Containers.Default.Configure(Container);            }            return Container;        }    }






在Facade層把Services層接口簡單“包裝”一下,然后就可以在Presentation層(Asp.Net MVC的Controller)使用了,代碼我就不寫了,上篇和前幾篇都有講過了   


源碼:所有實現(xiàn)代碼都貼出來了,且也偷了下懶沒按照慣例寫我們“土土的測試”。寫下篇的時候再上源碼吧:)  



發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 成人小视频在线播放 | 黄网站在线免费看 | 国产91小视频在线观看 | 在线高清中文字幕 | 亚洲 91| 黑人一级片视频 | 一区二区三区欧美在线 | av电影在线观看免费 | 免费a级网站 | 媚药按摩痉挛w中文字幕 | 成熟女人特级毛片www免费 | 国产亚洲美女精品久久久2020 | 国产精品久久99精品毛片三a | 国产精品成人一区二区三区电影毛片 | 久久区二区| 99精品在线观看 | 一级免费特黄视频 | 一色视频 | 99激情| 老师你怎么会在这第2季出现 | 亚洲电影在线观看高清免费 | lutube成人福利在线观看 | 激情宗合网 | 鲁丝一区二区三区不属 | 色播视频在线播放 | 羞羞视频免费网站男男 | 久久久aa| 毛片免费视频网站 | 性爱免费在线视频 | 免费国产在线观看 | 久久影院一区二区三区 | 亚洲视频在线视频 | 久久久久久久久久久久免费 | 精品视频在线免费看 | 精品在线视频观看 | av国产免费 | 一级看片免费视频 | 国产亚洲精品精 | 亚洲极色 | av国产免费 | 97中文字幕第一一一页 |