亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

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

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

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

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

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

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



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


對于事務的處理我希望顯示的用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個字典項,并且要綁定事務(當然這不可能是實際業務,只是為了方便代碼舉例),上面說了要放Services層,代碼如下:
Service接口(里面有個方法叫插2次....):

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

代碼
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:在接口的方法上進行標記,這樣繼承這個接口的類里實現這個接口方法的方法就能被攔截(有點拗口...)
在這里我們使用第三種方法InterfaceInterceptor,所以我們把接口的方法打上標記TransactionAttribute:   


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




Unity對AOP的實現還是要依賴于UnityContainer,也有兩種實現方式:代碼實現和XML配置實現,這里我們使用XML配置實現。
新建一個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)使用了,代碼我就不寫了,上篇和前幾篇都有講過了   


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



發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲aⅴ男人的天堂在线观看| 欧美国产欧美亚洲国产日韩mv天天看完整| 欧美日韩免费网站| 久久亚洲精品成人| 一区二区中文字幕| 成人黄色激情网| 91高清视频免费| 91探花福利精品国产自产在线| 欧美激情视频一区二区三区不卡| 亚洲护士老师的毛茸茸最新章节| 国产精品久久久久久久久久久不卡| 国内偷自视频区视频综合| 国产91av在线| 成人做爽爽免费视频| 欧美性猛交丰臀xxxxx网站| 精品国产91久久久久久| 日韩欧中文字幕| 少妇高潮久久77777| 亚洲天堂av女优| 91麻豆国产语对白在线观看| 国产亚洲精品综合一区91| 亚洲美女性生活视频| 久久久在线视频| 一区二区三区精品99久久| 亚洲一区中文字幕在线观看| 国产精品99久久久久久久久| 日韩女在线观看| 98精品在线视频| 88xx成人精品| 亚洲尤物视频网| 久久九九热免费视频| 亚洲第一精品电影| 97视频在线观看视频免费视频| 久久久影视精品| 国产99久久精品一区二区 夜夜躁日日躁| 91亚洲国产成人精品性色| 国产噜噜噜噜久久久久久久久| www.xxxx精品| 亚洲色图17p| 欧美一二三视频| 亚洲无限乱码一二三四麻| 国产亚洲精品久久久久久777| 国产精品成人av在线| 欧美一级片一区| 最新亚洲国产精品| 亚洲香蕉在线观看| 久久综合免费视频影院| 91精品久久久久久久| 精品国产成人av| 欧美激情免费视频| 欧美日韩亚洲精品一区二区三区| 国产精品成人av性教育| 日韩成人激情在线| 久久人体大胆视频| 91免费视频国产| 亚洲第一区在线| 92国产精品视频| 成人亚洲综合色就1024| 国产亚洲视频在线| 久久91亚洲精品中文字幕奶水| 欧美午夜视频在线观看| 国语自产精品视频在线看| 国产91在线播放九色快色| 国产97在线播放| 伦伦影院午夜日韩欧美限制| 日韩av在线网站| 国产欧美婷婷中文| 91九色蝌蚪国产| 欧美性高跟鞋xxxxhd| 国产成人av网址| 亚洲亚裔videos黑人hd| 91在线观看欧美日韩| 日韩av男人的天堂| 国产成人激情视频| 欧美性色19p| 色无极影院亚洲| 日本免费在线精品| 国产精品欧美一区二区三区奶水| 欧美激情一二三| 国产精品自拍偷拍| 国产精品www| 亚洲欧美一区二区三区在线| www.亚洲免费视频| 亚洲老头老太hd| 成人国产精品色哟哟| 动漫精品一区二区| 亚洲国产欧美一区| 九九热最新视频//这里只有精品| 亚洲国产精品热久久| 日韩少妇与小伙激情| 亚洲成年人在线播放| 国产精品免费一区二区三区都可以| 日韩美女视频免费在线观看| 久久久久这里只有精品| 国产精品成人观看视频国产奇米| 欧美性极品xxxx娇小| 亚洲精品综合久久中文字幕| 亚洲国产精品悠悠久久琪琪| 国产v综合ⅴ日韩v欧美大片| 91国偷自产一区二区三区的观看方式| 麻豆国产va免费精品高清在线| 国产精品视频永久免费播放| 色偷偷888欧美精品久久久| 国产精品国产自产拍高清av水多| 欧美精品久久久久久久免费观看| 亚洲久久久久久久久久久| 国产精品久久久久久亚洲影视| 欧美日韩亚洲精品一区二区三区| 一本色道久久综合亚洲精品小说| 日韩中文字幕在线精品| 欧美网站在线观看| 激情亚洲一区二区三区四区| 国产精品日韩欧美大师| 久久久免费精品视频| 岛国视频午夜一区免费在线观看| 97碰碰碰免费色视频| 国产精品久久久久免费a∨大胸| 国内精品小视频| 久久99精品久久久久久琪琪| 成人伊人精品色xxxx视频| 美日韩精品免费视频| 亚洲国产精品yw在线观看| 日韩视频在线免费观看| 久久久久这里只有精品| 国产91色在线| 国产精品美女午夜av| 国产成人av网| 69**夜色精品国产69乱| 亚洲精品自拍第一页| 中文字幕不卡在线视频极品| 欧美重口另类videos人妖| 国产成人精品999| 午夜精品久久久久久久久久久久久| 最新国产精品亚洲| 欧美二区在线播放| 国产精品中文字幕在线观看| 97人人模人人爽人人喊中文字| 日韩亚洲国产中文字幕| 亚洲一区二区在线| 亚洲精品久久久一区二区三区| 日韩av网站导航| 日韩精品在线电影| 91久久久国产精品| 国产在线精品播放| 中文字幕亚洲综合久久筱田步美| 日本中文字幕久久看| 亚洲视屏在线播放| 亚洲国产天堂久久综合网| 国产精品视频网站| 亚洲欧美在线第一页| 欧美剧在线观看| 亚洲综合小说区| 日韩国产高清视频在线| 成人在线视频网站| 亚洲国产天堂久久综合网| 欧美性猛交视频| 久久99精品久久久久久青青91| 亚洲自拍在线观看| 日韩欧美黄色动漫| 欧美刺激性大交免费视频| 国产精品久久久久av| 精品国产乱码久久久久久婷婷| 川上优av一区二区线观看| 国产国语videosex另类|