1. 程式人生 > >C#/.NET Unity靜態實現AOP功能——實際案例Demo程式碼

C#/.NET Unity靜態實現AOP功能——實際案例Demo程式碼

C# Unity依賴注入利用Attribute實現AOP功能

在做專案時,常常要對某個功能進行擴充套件,我們一般都是利用OOP的思想, 在原有的功能上進行擴充套件。

如果能用AOP思想去擴充套件,會使程式碼的整體框架更加穩定,我推薦Unity框架,接下來介紹一下如何使用。

1. 首先通過NuGet新增相關依賴

需要Unity和Unity.Interception

!!重要!!注意!!版本不要選太新版,Unity選5.6.0以前的,Interception也選5.2.0左右的即可,新版使用該案例會出現一些問題,如有解決方法請在評論指出

2. 新增namespace

using Unity;
using Unity.Interception.ContainerIntegration;
using Unity.Interception.Interceptors.InstanceInterceptors.InterfaceInterception;
using Unity.Interception.PolicyInjection.Pipeline;
using Unity.Interception.PolicyInjection.Policies;

3. 開始使用EntLib\PIAB Unity 實現動態代理

3.1 先寫好相關特性Attribute,必須繼承自HandlerAttribute

#region 特性
public class UserHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        ICallHandler handler = new UserHandler() { Order = this.Order };
        return handler;
    }
}

public class LogHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new LogHandler() { Order = this.Order };
    }
}

public class ExceptionHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new ExceptionHandler() { Order = this.Order };
    }
}

public class AfterLogHandlerAttribute : HandlerAttribute
{
    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new AfterLogHandler() { Order = this.Order };
    }
}
#endregion 特性

3.2 再寫特性對應的行為

注[1]Order:是特性執行的順序,具體看後面的操作例項

注[2]getNext:執行下一個函式先,如果getNext在前面,那麼就是主程式執行完再執行這個特性行為(需要注意Order順序會反過來),否則就是在特性前(Order順序正常排序)

注[3]input:輸入的引數,input[0]是第一個引數…以此類推

#region 特性對應的行為
public class UserHandler : ICallHandler
{
    public int Order { get; set; }
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        User user = input.Inputs[0] as User;
        if (user.Password.Length < 10)
        {
            return input.CreateExceptionMethodReturn(new Exception("密碼長度不能小於10位"));
        }
        Console.WriteLine("引數檢測無誤");


        IMethodReturn methodReturn = getNext()(input, getNext); //getNext.Invoke().Invoke(input, getNext);

        //Console.WriteLine("已完成操作");

        return methodReturn;
    }
}

public class LogHandler : ICallHandler
{
    public int Order { get; set; }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="input">方法呼叫的引數列表</param>
    /// <param name="getNext"></param>
    /// <returns></returns>
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        User user = input.Inputs[0] as User;
        string message = $"RegUser:Username:{user.Name},Password:{user.Password}";
        Console.WriteLine("日誌已記錄,Message:{0},Ctime:{1}", message, DateTime.Now);
        return getNext()(input, getNext);
    }
}


public class ExceptionHandler : ICallHandler
{
    public int Order { get; set; }
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        IMethodReturn methodReturn = getNext()(input, getNext);
        if (methodReturn.Exception == null)
        {
            Console.WriteLine("無異常");
        }
        else
        {
            Console.WriteLine($"異常:{methodReturn.Exception.Message}");
        }
        return methodReturn;
    }
}

public class AfterLogHandler : ICallHandler
{
    public int Order { get; set; }
    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
        IMethodReturn methodReturn = getNext()(input, getNext);
        User user = input.Inputs[0] as User;
        string message = string.Format("RegUser:Username:{0},Password:{1}", user.Name, user.Password);
        Console.WriteLine("完成日誌,Message:{0},Ctime:{1},計算結果{2}", message, DateTime.Now, methodReturn.ReturnValue);
        return methodReturn;
    }
}
#endregion 特性對應的行為

3.3 最後寫相關的業務(函式方法/功能)

注[1]Order:這裡特性的執行順序是按照Order從小到大排的

注[2]DIP原則:這裡最好使用依賴倒置原則來寫,操作抽象會使程式碼更加穩定

#region 業務
[UserHandlerAttribute(Order = 1)]        
[LogHandlerAttribute(Order = 2)]
[ExceptionHandlerAttribute(Order = 3)]
[AfterLogHandlerAttribute(Order = 4)]
public interface IUserProcessor
{
    void RegUser(User user);
    User GetUser(User user);
}

public class UserProcessor : IUserProcessor
{
    public void RegUser(User user)
    {
        Console.WriteLine("使用者已註冊。");
        //throw new Exception("11");
    }

    public User GetUser(User user)
    {
        return user;
    }
}
#endregion 業務

4. 具體操作實現

注[1]斷點:在下面註釋需要打斷點的那裡打斷點檢視具體執行順序,以便理解

public static void Show()
{
    User user = new User()
    {
        Name = "Eleven",
        Password = "12345678957576"
    };
    {
        UserProcessor processor = new UserProcessor();
        processor.RegUser(user);

        Console.WriteLine("*********************");
    }
    {

        IUnityContainer container = new UnityContainer();//宣告一個容器
        container.RegisterType<IUserProcessor, UserProcessor>();//宣告UnityContainer並註冊IUserProcessor
        IUserProcessor processor = container.Resolve<IUserProcessor>();
        processor.RegUser(user);//沒有AOP的,可在此打個斷點F11進入

	//配置AOP,固定套路
        container.AddNewExtension<Interception>()
			.Configure<Interception>()
            .SetInterceptorFor<IUserProcessor>(new InterfaceInterceptor());

        IUserProcessor userprocessor = container.Resolve<IUserProcessor>();//重新例項化

        Console.WriteLine("********************");
        userprocessor.RegUser(user);//有AOP的,可在此打個斷點F11進入
        userprocessor.GetUser(user);//有AOP的,可在此打個斷點F11進入
    }
}

5. 小結

在這裡的Unity實現,主要是用Unity內部的特性去實現AOP,本文介紹的實現方法是靜態的,如果需要動態的還需寫配置檔案。過幾天再寫一篇Unity實現IOC及AOP的動態配置。