首页后端开发ASP.NETUnity容器在asp.net mvc中的IOC应用及AOP应用

Unity容器在asp.net mvc中的IOC应用及AOP应用

时间2023-12-03 03:15:03发布访客分类ASP.NET浏览678
导读:《asp.net-mvc框架揭秘》一书中,有个示例,是使用unity容器来注入自定义的控制器工厂。代码示例可以自己去下载源码,在这里我就不说了。IOC容器的本质是解耦的实例化接口类,而如何做到解耦就是通过第三方容器来实例化,在这里是unit...

《asp.net-mvc框架揭秘》一书中,有个示例,是使用unity容器来注入自定义的控制器工厂。代码示例可以自己去下载源码,在这里我就不说了。IOC容器的本质是解耦的实例化接口类,而如何做到解耦就是通过第三方容器来实例化,在这里是unity容器,而不是在项目中实例化接口类。实例化的方法无非就是反射,Emit,表达式树,委托等四个方法。Unity容器的IOC使用主要是三个个方法:Register,Resolver,Dispose。前者注册接口和接口类,后者将接口类的实例化转移到第三方容器中实现。而这里的Dispose却是有点文章了。如果单单是控制台的应用项目,就不必多说,如果是在mvc框架中的话,我们的接口类的资源释放应该放在什么地方合适呢?微软unity开发小组给我们做了很好的解释,原文:https://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx 我们将Unity容器里面资源的释放与控制器的资源释放绑定在一起。如何用代码来表示?我们在基于Unity的控制器工厂中的GetControllerInstance中解析controllerType对象,而不是解析某个接口: (IController)this.UnityContainer.Resolve(controllerType); 尽管Unity容器是IOC框架,我们还是可以使用unity来做AOP,可以参考的官方资料:(5 - Interception using Unity)。 我们主要是通过集成ICallHandler接口来实现AOP,这个接口是unity给我们提供的,这个接口主要就是一个Invoke方法。继承自ICallHandler接口的类(TCalHandler),当通过接口(TIOCInterface)开始调用类(TIOCImple)中的方法时,就会开始调用类(TCalHandler)的Invoke方法。 在Invoke中,如果调用getNext()方法就会调用IOCImple标注了属性的方法。如果你的C#基础比较扎实,你对C#中的一个重要知识点-特性(attribute)应该就会有印象以及一定的了解。asp.net-mvc框架中的过滤器就是基于attribute实现的。那么在这里也是,我们需要调用unity给我们提供的一个特性attribute-HandlerAttribute,在这里我们调用我们基于ICallHandler的类。 DI是为了解耦的实例化接口,而AOP是横向的注入一些逻辑,我们可以在AOP里面实现DI,unity中的AOP模块默认会给我们实现DI,一旦我们实现了AOP,就相当于实现了DI。我会挑一些代码片段来解释。代码来自asp.net-mvc框架揭秘> > 的第14章S1401源码。首先我们实现自己自定义的控制器工厂:

public class UnityControllerFactory : DefaultControllerFactory
{

    public IUnityContainer UnityContainer {
     get;
     private set;
 }


    public UnityControllerFactory(IUnityContainer unityContainer)
    {
    
        this.UnityContainer = unityContainer;

    }


    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {

        if (null == controllerType)
        {
    
            return null;

        }
    
        return (IController)this.UnityContainer.Resolve(controllerType);

    }

}

之前说过的unity的DI对象 Resolve是在这里完成的。

我们定义了一个接口ITimeProvider和接口实现类DefaultTimeProvider,然后我们基于ICallHandler接口实现一个类:

public class CachingCallHandler : ICallHandler
{

    public int Order {
     get ;
     set ;
 }


    public TimeSpan ExpirationTime {
     get;
     private set;
 }


    public static TimeSpan DefaultExpirationTime {
     get;
     private set;
 }
    

    public static FuncMethodBase, object[], string>
 CacheKeyGenerator {
     get;
     private set;
 }


    // 静态构造函数,只调用一次,并且是最先调用的
    static CachingCallHandler()
    {
    
        DefaultExpirationTime = new TimeSpan(0, 5, 0);
    
        Guid prefix = Guid.NewGuid();
    

        CacheKeyGenerator = (method, inputs) =>

        {
    
            StringBuilder sb = new StringBuilder();


            sb.AppendFormat("{
0}
    : ", prefix);

            sb.AppendFormat("{
0}
    : ", method.DeclaringType);

            sb.AppendFormat("{
0}
    : ", method.Name);


            if (inputs != null)
            {

                foreach (var input in inputs)
                {
    
                    string hashCode = (input == null) ? "" : input.GetHashCode().ToString();

                    sb.AppendFormat("{
0}
    : ", hashCode);

                }

            }
    
            return sb.ToString().TrimEnd(':');

        }
    ;


    }


    public CachingCallHandler(TimeSpan? expirationTime=null)
    {
    
        this.ExpirationTime = expirationTime.HasValue ? expirationTime.Value : DefaultExpirationTime;

    }


    public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
    {
    
        MethodInfo targetMethod = (MethodInfo)input.MethodBase;


        if(targetMethod.ReturnType == typeof(void))
        {
    
            return getNext()(input, getNext);

        }
    

        object[] inputs = new object[input.Inputs.Count];
    
        input.Inputs.CopyTo(inputs, 0);
    
        string cacheKey = CacheKeyGenerator(targetMethod, inputs);
    

        object[] cachedResult = HttpRuntime.Cache.Get(cacheKey) as object[];


        if (null == cachedResult)
        {
    
            IMethodReturn realReturn = getNext()(input, getNext);

            if(null == realReturn.Exception)
            {

                HttpRuntime.Cache.Insert(cacheKey, new object[] {
 realReturn.ReturnValue }
    , null, DateTime.Now.Add(this.ExpirationTime), Cache.NoSlidingExpiration);

            }
    
            return realReturn;

        }

        return input.CreateMethodReturn(cachedResult[0], new object[] {
 input.Arguments }
    );


    }

}

Invoke方法的调用是在ITimeProvider对象调用其内的接口时候触发调用的。 接下来基于HandlerAttribute来实现一个类:

public class CachingCallHandlerAttribute : HandlerAttribute
{

    public TimeSpan? ExpirationTime {
     get;
     private set;
 }


    public CachingCallHandlerAttribute(string expirationTime ="")
    {

        if(!string.IsNullOrEmpty(expirationTime))
        {
    
            TimeSpan expirationTimeSpan;

            if(!TimeSpan.TryParse(expirationTime, out expirationTimeSpan))
            {
    
                throw new ArgumentException("输入过期时间(TimeSpan) 不合法");

            }
    
            this.ExpirationTime = expirationTimeSpan;

        }

    }


    public override ICallHandler CreateHandler(IUnityContainer container)
    {

        return new CachingCallHandler(this.ExpirationTime) {
 Order = this.Order }
    ;

    }

}
    

接下来我们Global.asax中,完成AOP的注入:

IUnityContainer UnityContainer= new UnityContainer()
.AddNewExtensionInterception>
    ()
.RegisterTypeITimeProvider, DefaultTimeProvider>
    ();
    

UnityContainer.ConfigureInterception>
    ()
.SetInterceptorForITimeProvider>
    (new InterfaceInterceptor());
    

UnityControllerFactory controllerFactory = new UnityControllerFactory(UnityContainer);
    

ControllerBuilder.Current.SetControllerFactory(controllerFactory);

最后就是使用了:

public class HomeController : Controller
{
    
    public ITimeProvider TimeProvider;

    public HomeController(ITimeProvider _time)
    {
    
        TimeProvider = _time;

    }


    public void Index()
    {
    
        for (int i = 0;
     i  3;
 i++)
        {

            Response.Write(string.Format("{
0}
: {
1: hh:mm:ss}
    br/>
    ", "Utc", this.TimeProvider.GetCurrentTime(DateTimeKind.Utc)));
    
            Thread.Sleep(1000);


            Response.Write(string.Format("{
0}
: {
1: hh:mm:ss}
    br/>
    br/>
    ", "Local", this.TimeProvider.GetCurrentTime(DateTimeKind.Local)));
    
            Thread.Sleep(1000);

        }

    }

}

在这里TimeProvider调用期内的方法GetCurrentTime方法时就会调用Invoke方法。Invoke方法的参数GetNextHandlerDelegate类的变量在Invoke中的调用代表着真正的调用GetCurrentTime方法。其实我们可以这么实现:

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
    
    
    var retvalue = getNext()(input, getNext);

    if(retvalue.Exception!=null)
    {
    
        Console.WriteLine("error");

    }
    
    return retvalue;

}
    

这种简单的实现是完全可以的。

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Unity容器在asp.net mvc中的IOC应用及AOP应用
本文地址: https://pptw.com/jishu/565638.html
asp.net core选项Options模块的笔记 asp.net core 上使用redis探索(3)--redis示例demo

游客 回复需填写必要信息