Aop介紹及幾種實現方式

来源:https://www.cnblogs.com/flyingaway/archive/2017/12/28/8126178.html
-Advertisement-
Play Games

本文主要是介紹Aop的一些概念,並結合實際編程,介紹了靜態織入和動態織入的幾種實現方式,並做了相應比較,從而讓讀者可以更好的選擇適合自己實際情況的技術方案。 ...


目錄

1.Aop介紹

2.Aop的基本概念

3.Aop的織入方式

4.Aop之靜態織入

5.Aop之動態織入

  a.使用.net真實代理實現織入

  b.使用Unity框架的攔截器實現織入

 

 

Aop介紹

     我們先看一下wiki百科的介紹
     Traditional software development focuses on decomposing systems into units of primary functionality, while recognizing that there are other issues of concern that do not fit well into the primary decomposition. The traditional development process leaves it to the programmers to code modules corresponding to the primary functionality and to make sure that all other issues of concern are addressed in the code wherever appropriate. Programmers need to keep in mind all the things that need to be done, how to deal with each issue, the problems associated with the possible interactions, and the execution of the right behavior at the right time. These concerns span multiple primary functional units within the application, and often result in serious problems faced during application development and maintenance. The distribution of the code for realizing a concern becomes especially critical as the requirements for that concern evolve – a system maintainer must find and correctly update a variety of situations.

    Aspect-oriented software development focuses on the identification, specification and representation of cross-cutting concerns and their modularization into separate functional units as well as their automated composition into a working system.

     傳統的軟體開發關註將系統分解成為一個主要的功能單元,然而卻發現一些問題並不適合分解方式。這種傳統的開發過程讓編碼人員合作編寫主要的功能模塊,以此保證那些主要的關註點能夠正確的被編碼。編碼人員需要記住所有需要被完成的事情,如何處理每個問題,問題可能的關聯關係,以此確定代碼在正確的時候以正確的方式被執行。這些關註點在應用裡面跨域了多個主要供單元,這經常在開發和維護時引發一些嚴重的問題。這些分散式代碼導致的問題變得越來越迫切得需要得到解決-一個系統維護人員必須解決這種問題。

    面向切麵軟體開發需要關註這些識別的,詳細的,具有代表性的切麵問題,將其模塊化到功能搗衣並且自動將這些代碼組合到一個工作中的系統。

 

    英語比較蹩腳,翻譯比較澀,總結起來的意思就是,Aop是將一些已經識別的切麵關註的功能封裝,並能自動將該功能組合到需要的地方。

    我對Aop的理解就是,一些被封裝好的,通用的業務單元,真正的編程人員不需要關註的部分,並能動態或靜態的將這部分功能組合到業務中去。舉個簡單的例子,我們在代碼中,經常要判斷是否用戶登錄,如果未登錄,就需要跳轉到指定的頁面,偽代碼如下:

 

public string GetNews(){
            /*判斷是否登錄,如果已經登錄,則執行後面的業務代碼
             如果沒有登錄,則跳轉到登錄頁面*/
             
            //業務代碼
}

    我們可以來看一下簡單的流程圖

    從圖中我們可以將代碼中的登錄的判斷業務分解成一個單獨的業務單元,在需要的地方打上一個標簽,告訴系統這裡需要執行,那麼其他編碼人員就不需要再寫重覆類似的代碼了。這就是Aop解決的問題。

Aop的基本概念

    在介紹Aop的實現方式前,我們先瞭解一下Aop的幾個知識點,這有助於我們理解Aop的實際技術。

 

1)aspect(切麵):實現了cross-cutting功能,是針對切麵的模塊。最常見的是logging模塊,這樣,程式按功能被分為好幾層,如果按傳統的繼承的話,商業模型繼承日誌模塊的話根本沒有什麼意義,而通過創建一個logging切麵就可以使用AOP來實現相同的功能了。 2)jointpoint(連接點):連接點是切麵插入應用程式的地方,該點能被方法調用,而且也會被拋出意外。連接點是應用程式提供給切麵插入的地方,可以添加新的方法。比如以上我們的切點可以認為是findInfo(String)方法。 3)advice(處理邏輯):advice是我們切麵功能的實現,它通知程式新的行為。如在logging里,logging advice包括logging的實現代碼,比如像寫日誌到一個文件中。advice在jointpoint處插入到應用程式中。以上我們在MyHandler.java中實現了advice的功能 4)pointcut(切點):pointcut可以控制你把哪些advice應用於jointpoint上去。決定了那個jointpoint會獲得通知。 5)introduction:允許添加新的方法和屬性到類中。 6)target(目標類):是指那些將使用advice的類,一般是指獨立的那些商務模型。比如以上的StudentInfoServiceImpl. 7)proxy(代理類):使用了proxy的模式。是指應用了advice的對象,看起來和target對象很相似。

8)weaving(插入):是指應用aspects到一個target對象創建proxy對象的過程:complie time,classload time,runtime

 

以上這幾點是從百度百科中找來的,如果暫時不理解沒有關係,後面我會結合實際代碼講解,看完具體代碼,再回來這裡來對號入座,相信大家可以很快理解這接概念。

Aop的倆種織入方式

   目前在.NET平臺中,支持的織入方式有倆中,一種是靜態織入,即編譯時織入,另外一個是動態織入,即運行時織入。倆中方式各有優缺點,使用靜態織入,可以不破壞代碼結構,這裡的破壞代碼結構是你需要使用多餘配置,寫一些多餘的代碼,或必須依賴某種方式(這裡大家也許也還不太明白,可以看完後面倆種方式的具體代碼比較,再回頭來看,會比較好理解)。使用動態織入的優點就是可以動態調試。倆中織入方式是互補的,即動態織入的優點也是靜態織入的缺點,同理,靜態織入的優點亦是動態織入的缺點。大家在做技術選型時可以根據自己的實際情況進行選擇。

Aop之靜態織入

    目前成熟的框架有PostSharp,這個框架是商業框架,意思就是需要付費,這裡就不具體介紹了,需要瞭解的土豪請到官網查看,具體如何使用請查閱文檔

    BSF.Aop  .Net 免費開源,靜態Aop織入(直接修改IL中間語言)框架,類似PostSharp(收費),實現前後Aop切麵和INotifyPropertyChanged註入方式。其原理是在編譯生成IL後,藉助Mono.Cecil的AssemblyDefinition讀取程式集,並檢測需要註入的點,並將指定的代碼註入到程式集中。有想具體深入研究的同學,可以到 BSF.Aop中下載源碼進行研究。遺憾的是這個只實現了倆個切入點,並沒有在異常時提供切入點。

    我們模擬一個日誌記錄的例子,我們先建一個項目。

1.    在項目中引用BSF.Aop.dll,Mono.Cecil.dll,Mono.Cecil.Pdb.dll,Microsoft.Build.dll;

2.    添加一個類LogAttribute並繼承Aop.Attributes.Around.AroundAopAttribute(切麵);

3.    重寫AroundAopAttribute的Before和After方法,並寫入邏輯代碼;

4.    新建一個測試類LogTest,並添加Execute方法,併在Execute方法上面添加LogAttribute標簽;

5.    我們在main裡面new一個LogTest對象並調用看看輸出結果;

具體的代碼如下:

 public class LogTest
    {
        [LogAttribute]
        public void Execute(int a)
        {
            a = a * 100;
            System.Console.WriteLine("Hello world!" + a);
        }
    }

    public class LogAttribute : AroundAopAttribute
    {
        public virtual void Before(AroundInfo info)
        {
            System.Console.WriteLine("Log before executed value is" + info.Params["a"]);
        }
        
        public virtual void After(AroundInfo info)
        {
            System.Console.WriteLine("Log after executed value is" + info.Params["a"]);
        }
    }
 static void Main(string[] args)
{
    Aop.AopStartLoader.Start(null);

     new LogTest().Execute(2);

     Console.ReadLine();
}    

執行代碼輸出:

    上例代碼中

  •     aspect 日誌
  •     join point 即AroundAopAttribute中的Before和After,即方法執行前和方法執行後
  •     advice 即日誌的邏輯部分
  •     pointcut 即我們LogAttribute中的Before(上面的例子故意沒有重寫After,是因為怕大家誤解)
  •     target 這裡我們是對方法進行切入的,即Execute方法
  •     weaving 這個例子中我們採用的是編譯時的織入

Aop之動態織入

     使用.NET提供的遠程代理,即RealProxies來實現。

1.先建一個Aop代理類AopClassAttribute繼承於ProxyAttribute,這個標簽會告訴代理,這個類需要被代理創建調用;

/// <summary>
    /// 標記一個類為Aop類,表示該類可以被代理註入
    /// </summary>
    public class AopClassAttribute : ProxyAttribute
    {
        public override MarshalByRefObject CreateInstance(Type serverType)
        {
            AopProxy realProxy = new AopProxy(serverType);
            return realProxy.GetTransparentProxy() as MarshalByRefObject;
        }
    }

2.定義Aop的屬性,並定義織入點

/// <summary>
    /// Attribute基類,通過實現該類來實現切麵的處理工作
    /// </summary>
    public abstract class AopAttribute : Attribute
    {
        /// <summary>
        /// 調用之前會調用的方法
        /// 1.如果不需要修改輸出結果,請返回null
        /// 2.如果返回值不為null,則不會再調用原始方法執行,而是直接將返回的參數作為結果
        /// </summary>
        /// <param name="args">方法的輸入參數列表</param>
        /// <param name="resultType">方法的返回值類型</param>
        public abstract object PreCall(object[] args, Type resultType);
        /// <summary>
        /// 調用之後會調用的方法
        /// </summary>
        /// <param name="resultValue">方法的返回值</param>
        /// <param name="args">方法的輸入參數列表</param>
        public abstract void Called(object resultValue, object[] args);
        /// <summary>
        /// 調用出現異常時會調用的方法
        /// </summary>
        /// <param name="e">異常值</param>
        /// <param name="args">方法的輸入參數列表</param>
        public abstract void OnException(Exception e, object[] args);
    }

 3.定義代理的邏輯過程,這裡我對returnvalue做了判斷,是為了實現緩存更新和添加的切麵代碼做的,在這裡我實現了三個切入點的調用,具體可看註釋部分

/// <summary>
    /// 主要代理處理類
    /// </summary>
    internal class AopProxy : RealProxy
    {
        public AopProxy(Type serverType)
            : base(serverType)
        {
        }

        public override IMessage Invoke(IMessage msg)
        {
            if (msg is IConstructionCallMessage) return InvokeConstruction(msg);
            else return InvokeMethod(msg);
        }

        private IMessage InvokeMethod(IMessage msg)
        {
            IMethodCallMessage callMsg = msg as IMethodCallMessage;
            IMessage returnMessage;
            object[] args = callMsg.Args;
            var returnType = (callMsg.MethodBase as System.Reflection.MethodInfo).ReturnType;//方法返回類型
            object returnValue = null;//方法返回值
            AopAttribute[] attributes = callMsg.MethodBase.GetCustomAttributes(typeof(AopAttribute), false) as AopAttribute[];
            try
            {
                if (attributes == null || attributes.Length == 0) return InvokeActualMethod(callMsg);
          //前切點
                foreach (AopAttribute attribute in attributes)
                    returnValue = attribute.PreCall(args, returnType);
                //如果以前切麵屬性都沒有返回值,則調用原始的方法;否則不調用
                //主要是做緩存類似的業務
                if (returnValue == null)
                {
                    returnMessage = InvokeActualMethod(callMsg);
                    returnValue = (returnMessage as ReturnMessage).ReturnValue;
                }
                else returnMessage = new ReturnMessage(returnValue, args, args.Length, callMsg.LogicalCallContext, callMsg);
          //後切點
                foreach (AopAttribute attribute in attributes)
                    attribute.Called(returnValue,args);
            }
            catch (Exception e)
            {
         //異常切入點
foreach (AopAttribute attribute in attributes) attribute.OnException(e, args); returnMessage = new ReturnMessage(e, callMsg); } return returnMessage; } private IMessage InvokeActualMethod(IMessage msg) { IMethodCallMessage callMsg = msg as IMethodCallMessage; object[] args = callMsg.Args; object o = callMsg.MethodBase.Invoke(GetUnwrappedServer(), args); return new ReturnMessage(o, args, args.Length, callMsg.LogicalCallContext, callMsg); } private IMessage InvokeConstruction(IMessage msg) { IConstructionCallMessage constructCallMsg = msg as IConstructionCallMessage; IConstructionReturnMessage constructionReturnMessage = this.InitializeServerObject((IConstructionCallMessage)msg); RealProxy.SetStubData(this, constructionReturnMessage.ReturnValue); return constructionReturnMessage; } }

 

 4.定義上下文邊界對象,想要使用Aop的類需要繼承此類(這個是這種Aop方式破壞性最大的地方,因為需要繼承一個類,而面向對象單繼承的特性導致了業務類不能再繼承其他的類。可以想象一下你有一個查詢基類,然後另一個查詢類想要繼承查詢基類,而又想使用Aop,這時就尷尬了);

/// <summary>
    /// Aop基類,需要註入的類需要繼承該類
    /// 對代碼繼承有要求,後續可以改進一下
    /// 註意,需要記錄的不支持上下文綁定,如果需要記錄,使用代理模式解決
    /// </summary>
    public abstract class BaseAopObject : ContextBoundObject
    {
    }

 

 5.定義Advice部分,即實際的業務邏輯,繼承於AopAttribute

public class IncreaseAttribute : AopAttribute
    {
        private int Max = 10;
        public IncreaseAttribute(int max)
        {
            Max = max;
        }
        public override object PreCall(object[] args, Type resultType)
        {
            if (args == null || args.Count() == 0 || !(args[0] is ExampleData)) return null;

            var data = args[0] as ExampleData;
            string numString = args[0].ToString();
            data.Num = data.Num * 100;
            Console.WriteLine(data.Num);
            return null;
        }

        public override void Called(object resultValue, object[] args)
        {
            if (args == null || args.Count() == 0 || !(args[0] is ExampleData)) return;

            var data = args[0] as ExampleData;
            string numString = args[0].ToString();
            data.Num = data.Num * 100;
            Console.WriteLine(data.Num);
        }

        public override void OnException(Exception e, object[] args)
        {

        }
    }

public class ExampleData
    {
        public int Num { get; set; }
    }

 

 6.完成了上面的部分,我們就可以來使用Aop了,定義一個需要使用Aop的類,繼承於BaseAopObject,併在類上面加上[AopClass],在需要切入的方法上加上剛纔定義的[IncreaseAttribute]

[AopClass]
    public class Example : BaseAopObject
    {
        [IncreaseAttribute(10)]
        public static void Do(ExampleData data)
        {
            Add(data);
        }

        [IncreaseAttribute(10)]
        public static ExampleData Add(ExampleData data)
        {
            return new ExampleData { Num = ++data.Num };
        }
    }

 

    可以看到,使用上面這種織入方式,對代碼的侵入性太大,會限制代碼的可擴展性。所以我比較不建議使用。

    另一種方式是藉助Ioc的代理來做Aop切麵註入,這裡我們以Unity作為Ioc容器,以之前寫的關於Unity Ioc中的例子來介紹Aop。

   1.添加AopAttribute(定義連接點),這裡有個迴圈引用,就是AopHandler和AopAttribute之間,不過並不影響使用,如有需要大家可以自己解決一下;

/// <summary>
    /// 標記一個類或方法為代理,表示該類或方法可以被代理
    /// </summary>
    [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public abstract class AopAttribute : HandlerAttribute
    {
        /// <summary>
        /// 請勿重寫該方法
        /// </summary>
        /// <param name="container"></param>
        /// <returns></returns>
        public override ICallHandler CreateHandler(IUnityContainer container)
        {
            return new AopHandler();
        }
        /// <summary>
        /// 調用之前會調用的方法
        /// 1.如果不需要修改輸出結果,請返回null,ouputs返回new object[0]
        /// 2.如果返回值不為null,則不會再調用原始方法執行,而是直接將返回的參數作為結果
        /// </summary>
        /// <param name="inputArgs">方法的輸入參數列表</param>
        /// <param name="outputs">方法中的out值,如果沒有請返回null</param>
        /// <returns>返回值</returns>
        public abstract object PreCall(object[] inputArgs, out object[] outputs);
        /// <summary>
        /// 調用之後會調用的方法
        /// </summary>
        /// <param name="resultValue">方法的返回值</param>
        /// <param name="inputArgs">方法的輸入參數列表</param>
        /// <param name="outputs">方法中的out值,如果沒有則該參數值為null</param>
        public abstract void Called(object resultValue, object[] inputArgs, object[] outputs);
        /// <summary>
        /// 調用出現異常時會調用的方法
        /// </summary>
        /// <param name="e">異常值</param>
        /// <param name="inputArgs">方法的輸入參數列表,鍵為參數名,值為參數值</param>
        public abstract void OnException(Exception e, Dictionary<string, object> inputArgs);
    }

 

 2.添加AopHandler(代理類);

/// <summary>
    /// 主要代理處理類
    /// </summary>
    internal class AopHandler : ICallHandler
    {
        public int Order { get; set; } = 1;

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            IMethodReturn returnValue = null;
            object attrReturnValue = null;
            object[] outputs = null;
            Dictionary<string, object> inputs = new Dictionary<string, object>();
            //假如有忽略特性,直接忽略,不進行AOP代理
            IgnoreAttribute[] ignoreAttributes = input.MethodBase.GetCustomAttributes(typeof(IgnoreAttribute), true) as IgnoreAttribute[];
            if (ignoreAttributes != null && ignoreAttributes.Length > 0) return input.CreateMethodReturn(attrReturnValue, outputs);

            AopAttribute[] attributes = input.MethodBase.GetCustomAttributes(typeof(AopAttribute), true) as AopAttribute[];
            try
            {
                if (attributes == null || attributes.Length == 0) return getNext()(input, getNext);

                for (var i = 0; i < input.Arguments.Count; i++) inputs.Add(input.Inputs.ParameterName(i), input.Inputs[i]);

                foreach (AopAttribute attribute in attributes)
                    attrReturnValue = attribute.PreCall(inputs.Values.ToArray(), out outputs);
                //如果以前切麵屬性都沒有返回值,則調用原始的方法;否則不調用
                //主要是做緩存類似的業務
                if (attrReturnValue == null)
                {
                    returnValue = getNext()(input, getNext);
                    outputs = new object[returnValue.Outputs.Count];
                    for (var i = 0; i < returnValue.Outputs.Count; i++) outputs[i] = returnValue.Outputs[i];
                }
                else returnValue = input.CreateMethodReturn(attrReturnValue, outputs);

                if (returnValue.Exception != null) throw returnValue.Exception;

                foreach (AopAttribute attribute in attributes)
                    attribute.Called(returnValue.ReturnValue, inputs.Values.ToArray(), outputs);
            }
            catch (Exception e)
            {
                foreach (AopAttribute attribute in attributes)
                    attribute.OnException(e, inputs);
                returnValue = input.CreateExceptionMethodReturn(e);
            }
            return returnValue;
        }
    }

 3..定義一個我們自己的功能塊(業務邏輯),這裡還是以日誌為例;

public class LogAttribute : AopAttribute
    {
        public override void Called(object resultValue, object[] inputArgs, object[] outputs)
        {
            Console.WriteLine("Called");
        }

        public override void OnException(Exception e, Dictionary<string, object> inputArgs)
        {
            Console.WriteLine("exception:" + e.Message);
        }

        public override object PreCall(object[] inputArgs, out object[] outputs)
        {
            Console.WriteLine("PreCall");
            outputs = new object[0];
            return null;
        }
    }

 

 5.接下來我們稍微改造一下我們的印鈔機;

 /// <summary>
    /// 印鈔機
    /// </summary>
    public class CashMachine
    {
        public CashMachine() { }

        public void Print(ICashTemplate template)
        {
            string templateContent = template.GetTemplate("人民幣");

            System.Console.WriteLine(templateContent);
        }
    }
    /// <summary>
    /// 印鈔模塊
    /// </summary>
    public interface ICashTemplate
    {
        /// <summary>
        /// 獲取鈔票模板
        /// </summary>
        /// <returns></returns>
        [Log]
        string GetTemplate(string flag);
    }

    /// <summary>
    /// 人民幣鈔票模板
    /// </summary>
    public class CNYCashTemplate : ICashTemplate
    {
        public CNYCashTemplate() { }
        public string GetTemplate(string flag)
        {
            return "這是人民幣模板!" + flag + "  這是返回值。";
        }
    }
    /// <summary>
    /// 美鈔鈔票模板
    /// </summary>
    public class USDCashTemplate : ICashTemplate
    {
        public USDCashTemplate() { }

        public string GetTemplate(string flag)
        {
            throw new Exception("哎呀,美鈔模板有問題呀!");
        }
    }

 

6.然後我們在命令行的Main里改造一下;

static void Main(string[] args)
        {
            try
            {
                ICashTemplate usdTemplate = new USDCashTemplate();
                ICashTemplate rmbTemplate = new CNYCashTemplate();
                new CashMachine().Print(rmbTemplate);
                new CashMachine().Print(usdTemplate);
            }
            catch (Exception)
            {
            }
            Console.ReadLine();
        }

 

7.啟動一下看看結果

8.可以看到,只輸出了GetTemplate方法的輸出,並沒有輸出日誌,我們要使用Ioc來註冊對象才能使用,繼續改造Main方法;

 static void Main(string[] args)
        {
            UnityContainer container = new UnityContainer();
            container.AddNewExtension<Interception>().RegisterType<ICashTemplate, CNYCashTemplate>("cny");
            container.Configure<Interception>().SetInterceptorFor<ICashTemplate>("cny", new InterfaceInterceptor());
            container.AddNewExtension<Interception>().RegisterType<ICashTemplate, USDCashTemplate>("usd");
            container.Configure<Interception>().SetInterceptorFor<ICashTemplate>("usd", new InterfaceInterceptor());

            try
            {new CashMachine().Print(container.Resolve<ICashTemplate>("cny"));
                new CashMachine().Print(container.Resolve<ICashTemplate>("usd"));
            }
            catch (Exception)
            {
            }
            Console.ReadLine();
        }

 

 9.啟動運行,看一下結果;

可以看到,三個方法都執行了,而在拋出異常時是不會執行Called的方法的;

10.上面我們是直接使用了UnityContainer來註冊對象,而沒有使用我們之前封裝的Ioc,我們還有更簡單的方式,就是採用配置的方式來註冊對象和攔截器實現Aop。在實際,使用一個單獨的文件來配置ioc會更易於維護。我們先添加一個unity.config文件;

<?xml version="1.0" encoding="utf-8" ?>
<unity xmlns= "http://schemas.microsoft.com/practices/2010/unity ">
  <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/>
  <!--註入對象-->
  <typeAliases>
    <!--表示單例-->
    <typeAlias alias="singleton" type="Unity.Lifetime.ContainerControlledLifetimeManager,Unity.Abstractions" />
    <!--表示每次使用都進行創建-->
    <typeAlias alias="transient" type="Unity.Lifetime.TransientLifetimeManager,Unity.Abstractions" />
  </typeAliases>
  <container name= "Default">
    <extension type="Interception"/>
    <!--type表示介面 格式為 帶命名空間的介面,程式集名 mapTo表示需要註入的實體類 name表示註入實體的name-->
    <register type= "IocWithUnity.ICashTemplate,IocWithUnity" mapTo= "IocWithUnity.CNYCashTemplate,IocWithUnity" name="cny">
      <!--定義攔截器-->
      <interceptor type="InterfaceInterceptor"/>
      <policyInjection/>
      <!--定義對象生命周期-->
      <lifetime type="singleton" />
    </register>
    <!--type表示介面 格式為 帶命名空間的介面,程式集名 mapTo表示需要註入的實體類 name表示註入實體的name-->
    <register type= "IocWithUnity.ICashTemplate,IocWithUnity" mapTo= "IocWithUnity.USDCashTemplate,IocWithUnity" name="usd">
      <!--定義攔截器-->
      <interceptor type="InterfaceInterceptor"/>
      <policyInjection/>
      <!--定義對象生命周期-->
      <lifetime type="singleton" />
    </register>
  </container>
</unity>

 

11.再配置app.config(WEB項目應該是web.config);

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/>
  </configSections>
  <unity configSource="unity.config"/>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

 

12.將我們之前寫的IocContainer修改一下讀取配置;

 public static class IocContainer
    {
        private static IUnityContainer _container = null;
        static IocContainer()
        {
            _container = new UnityContainer();

            object unitySection = ConfigurationManager.GetSection("unity");
            if (unitySection == null) return;

            UnityConfigurationSection section = (UnityConfigurationSection)unitySection;
            section.Configure(_container, "Default");
        }
        /// <summary>
        /// 註冊一個實例作為T的類型
        /// </summary>
        /// <typeparam name="T">需要註冊的類型</typeparam>
        /// <param name="instance">需要註冊的實例</param>
        public static void Register<T>(T instance)
        {
            _container.RegisterInstance<T>(instance);
        }
        /// <summary>
        /// 註冊一個名為name的T類型的實例
        /// </summary>
        /// <typeparam name="T">需要註冊的類型</typeparam>
        /// <param name="name">關鍵字名稱</param>
        /// <param name="instance">實例</param>
        public static void Register<T>(string name, T instance)
        {
            _container.RegisterInstance(name, instance);
        }
        /// <summary>
        /// 將類型TFrom註冊為類型TTo
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        public static void Register<TFrom, TTo>() where TTo : TFrom
        {
            _container.RegisterType<TFrom, TTo>();
        }
        /// <summary>
        /// 將類型TFrom註冊為類型TTo
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        /// <typeparam name="lifetime"></typeparam>
        public static void Register<TFrom, TTo>(LifetimeManager lifetime) where TTo : TFrom
        {
            _container.RegisterType<TFrom, TTo>(lifetime);
        }
        /// <summary>
        ///
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在學python學了簡單的從網上抓取圖片:剛好做一個C#版本的: 下麵貼代碼: using System;using System.IO;using System.Collections.Generic;using static System.Console;using System.Text; ...
  • 比如,某一個陣列中,有重覆的元素,我們想去除重覆的,保留一個。HashSet<T>含不重覆項的無序列表,從MSDN網上瞭解到,這集合基於散列值,插入元素的操作非常快。你可以寫一個方法: class Bn { public string[] Data { get; set; } public stri ...
  • 上一章,我們實現了用戶的註冊和登錄,登錄之後展示的是我們的主頁,頁面的左側是多級的導航菜單,定位並展示用戶需要訪問的不同頁面。目前導航菜單是寫死的,考慮以後菜單管理的便捷性,我們這節實現下可視化配置菜單的功能,這樣以後我們可以動態的配置導航菜單,不用再編譯發佈網站程式了。 增加後臺管理模塊 第1步, ...
  • 生產者發送消息到隊列,然後隊列(rabbitmq)把消息發送給消費者(消費者向rabbitmq索取消息) ...
  • 在控制臺中,分別輸入2個值,然後對其進行交換對調。 class Bm { public void SwapData() { string value1, value2, dumpValue; Console.WriteLine("輸入第一個值:"); value1 = Console.ReadLin ...
  • 原想在 MVC Action 上加一個自定義 Attribute 來做一些控制操作,最先的做法是在自定 Attribute 中定義一個屬性來做邏輯判斷,可惜事與願違,這個屬性值居然會被緩存起來,於是於此做個筆記以免後續重蹈覆轍。過濾器部分過濾器中定義了一個名稱為 count 的屬性值來初始化,並重寫... ...
  • 序列化(SerializeObject)與反序列化(DeserializeObject)。 打開Nuget安裝Newtonsoft.Json: class Bl { public void SerialAndDeser() { List<string> a = new List<string>() ...
  • public static class BubbleSortTool { public static void BubbleSort(this T[] array, AscendingorDescending ascendingorDescending) where T:IComparable { ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...