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

之前说过的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 });

    }
}

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()
.AddNewExtension<Interception>()
.RegisterType<ITimeProvider, DefaultTimeProvider>();

UnityContainer.Configure<Interception>()
.SetInterceptorFor<ITimeProvider>(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;
}

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

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

原文地址:https://www.cnblogs.com/zhiyong-ITNote/p/9013417.html

时间: 2024-11-09 02:02:07

Unity容器在asp.net mvc中的IOC应用及AOP应用的相关文章

IOC 容器在 ASP.NET MVC 中的应用

IOC:Inversion Of Control 翻译为控制反转,我们在面向对象软件开发过程中,一个应用程序它的底层结构可能由N种不同的构件来相互协作来完成我们定义的系统的业务逻辑.哪么每一个构件可能相互独立和相互依赖,如果相互依赖的构件中的某一个构件出现异常,就会影响到整个系统的稳定运行,对象之间的耦合关系是无法避免的,也是必要的,因为这是协同工作的基础.如何降低系统之间.模块之间和对象之间的耦合度,是软件工程永远追求的目标之一.为了解决对象之间的耦合度过高的问题,IOC 的理念被提出,并被成

Ioc容器Autofac系列(2)-- asp.net mvc中整合autofac(转)

经过上篇蜻蜓点水的介绍后,本篇通过实例快速上手autofac,展示当asp.net mvc引入了autofac之后会带来什么. 创建Asp.net MVC并引入Autofac 首先,创建一个MVC站点,为方便起见,选初始带HomeController和AccountController的那种.然后通过NuGet或到Autofac官网下载来引入类库.个人推荐前者,因为从VS2010开始,已内集可视化的NuGet功能,使用起来非常方便.如下图所示: 这是vs2012的界面,点击"Manage NuG

[转] ASP.NET MVC 中你必须知道的 13 个扩展点

ScottGu 在其 最新的博文 中推荐了 Simone Chiaretta 的文章 13 ASP.NET MVC extensibility points you have to know,该文章为我们简单介绍了  ASP.NET MVC  中的 13 个扩展点.Keyvan Nayyeri(与Simone合著了 Beginning ASP.NET MVC 1.0 一书)又陆续发表了一些文章,对这13个扩展点分别进行深入的讨论.我将在以后的随笔中对这些文章逐一进行翻译,希望能对大家有所帮助.

在ASP.NET MVC中使用Castle Windsor

平常用Inject比较多,今天接触到了Castle Windsor.本篇就来体验其在ASP.NET MVC中的应用过程. Visual Studio 2012创建一个ASP.NET MVC 4网站. 通过NuGet安装Castle Windsor. 在当前项目下创建一个名称为"IOC"的文件夹. 在ASP.NET MVC中,每次请求,DefaultControllerFactory都会为我们创建controller实例,我们需要自定义一个派生自DefaultControllerFact

ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入)

前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期. 这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 目录 ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器) ASP.NET Core中使用IOC三部曲(二.采用Autofac来替换IOC容器,并实现属性注入) ASP.NET Core中使用IOC三部曲(三.采用替换后的Autofac来实现AOP拦截) 正文 上

log4net 使用总结- (2)在ASP.NET MVC 中使用

log4net在ASP.NET MVC中的配置,还有一种配置方式,即不在web.config中,而是单独新建一个log4net.config 在根目录下 第一.引用log4net.dll 第二.在站点根目录下增加log4net.config <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="

ASP.NET MVC中使用异步控制器

线程池 一直想把项目改写成异步,但是ASP.NETMVC3下写的过于繁琐,.NET 4.5与ASP.NET MVC下代码写起来就比较简单了, MS好像也一直喜欢这样搞,每一个成熟的东西,都要演变好几个版本,才能趋于规范. ASP.NET MVC 中为什么需要使用异步呢,IIS有一个线程池来处理用户的请求,当一个新的请求过来时,将调度池中的线程以处理该请求,然而,但并发量很高的情况下,池中的线程已经不能够满足这么多的请求时候,池中的每一个线程都处于忙的状态则在处理请求时将阻塞处理请求的线程,并且该

ASP.NET MVC中使用ASP.NET AJAX异步访问WebService

使用过ASP.NET AJAX的朋友都知道,怎么通过ASP.NET AJAX在客户端访问WebService,其实在ASP.NET MVC中使用ASP.NET AJAX异步访问WebService 也没什么大的差别. 在ASP.NET应用程序里使用ASP.NET AJAX访问WebService通常都是通过ScriptMananger引入WebService生成客户端代理的方法,同时也可以使用Microsoft Ajax Library来完成.本文将介绍在ASP.NET MVC中使用ASP.NE

ASP.NET MVC中使用FluentValidation验证实体

1.FluentValidation介绍 FluentValidation是与ASP.NET DataAnnotataion Attribute验证实体不同的数据验证组件,提供了将实体与验证分离开来的验证方式,同时FluentValidation还提供了表达式链式语法. 2.安装FluentValidation FluentValidation地址:http://fluentvalidation.codeplex.com/ 使用Visual Studio的管理NuGet程序包安装FluentVa