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


    《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);
        }
    }
    View Code

    之前说过的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 Func<MethodBase, 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 });
    
        }
    }
    View Code

    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 };
        }
    }
    View Code

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

    IUnityContainer UnityContainer= new UnityContainer()
    .AddNewExtension<Interception>()
    .RegisterType<ITimeProvider, DefaultTimeProvider>();
    
    UnityContainer.Configure<Interception>()
    .SetInterceptorFor<ITimeProvider>(new InterfaceInterceptor());
    
    UnityControllerFactory controllerFactory = new UnityControllerFactory(UnityContainer);
    
    ControllerBuilder.Current.SetControllerFactory(controllerFactory);
    View Code

    最后就是使用了:

    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);
            }
        }
    }
    View Code

    在这里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;
    }
    View Code

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

    建议看看微软官方的资料https://msdn.microsoft.com/en-us/library/ff647202.aspx
    代码地址:链接:https://pan.baidu.com/s/1q98_Otwt1YC_00z_xcavIA 密码:b9pj

  • 相关阅读:
    詩与胡說张爱玲
    dragscroll 鼠标拖动滚动页面 userjs
    鼠标拖动控制页面滚动 (可运用于全屏触摸设备)
    给Webbrowser 添加用户脚本
    .net 4 环境调用 .net 2 编译的DLL
    webservice远程测试显示 “测试窗体只能用于来自本地计算机的请求”
    解决WebBrowser.DocumentCompleted事件的多次调用
    能够播放地球上现有的几乎所有格式 - VLC – 支持鼠标手势的播放器
    使 WebBrowser 更简单的插入、调用执行脚本, 可安装 jQuery 脚本的 C# 开源代码 IEBrowser
    “暂时没有对您所在的地区提供下载和试听服务”的解决办法
  • 原文地址:https://www.cnblogs.com/zhiyong-ITNote/p/9013417.html
Copyright © 2020-2023  润新知