IOC application and AOP application of Unity container in asp.net mvc

In the book "Asp.net-mvc Framework Demystified", there is an example of using the unity container to inject a custom controller factory. For code examples, you can download the source code yourself, so I won't talk about it here. The essence of the IOC container is to decouple the instantiated interface class, and how to achieve decoupling is to instantiate it through a third-party container, here it is the unity container, rather than instantiating the interface class in the project. The methods of instantiation are nothing more than four methods: reflection, Emit, expression tree, and delegation. The IOC of the Unity container mainly uses three methods: Register, Resolver, Dispose. The former registers the interface and the interface class, and the latter transfers the instantiation of the interface class to the third-party container for implementation. The Dispose here is a bit of an article. If it is just the application project of the console, there is no need to say more. If it is in the mvc framework, where should the resource release of our interface class be placed? The Microsoft unity development team gave us a good explanation, the original text: https://msdn.microsoft.com/en-us/library/dn178463(v=pandp.30).aspx
We will release the resources in the Unity container with The controller's resource release is tied together. How to express it in code? Instead of resolving an interface, we resolve the controllerType object in GetControllerInstance in the Unity-based controller factory:
(IController)this.UnityContainer.Resolve(controllerType);
Although the Unity container is an IOC framework, we can still use Unity to do AOP , you can refer to the official information: (5 - Interception using Unity) .
We mainly implement AOP by integrating the ICallHandler interface. This interface is provided to us by unity. This interface is mainly an Invoke method. The class (TCalHandler) inherited from the ICallHandler interface, when the method in the class (TIOCImple) is called through the interface (TIOCInterface), the Invoke method of the class (TCalHandler) will be called.
In Invoke, if the getNext() method is called, the method of the IOCImple annotated property will be called. If your C# foundation is relatively solid, you should have an impression and a certain understanding of an important knowledge point in C# - attributes. The filters in the asp.net-mvc framework are implemented based on attributes. So here too, we need to call a feature attribute-HandlerAttribute provided by unity, where we call our class based on ICallHandler.
DI is an instantiated interface for decoupling, and AOP is to inject some logic horizontally. We can implement DI in AOP. The AOP module in unity will implement DI for us by default. Once we implement AOP, it is equivalent to implementing DI. DI. I'll pick some code snippets to explain. The code comes from the source code of Chapter 14 S1401 of <<asp.net-mvc framework revealing>>. First we implement our own custom controller factory:

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

The Unity DI object Resolve mentioned earlier is done here.

We define an interface ITimeProvider and the interface implementation class DefaultTimeProvider, and then we implement a class based on the ICallHandler interface:

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 constructor, called only once, and is the first 
    static CachingCallHandler() called
    {
        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

The call of the Invoke method is triggered when the ITimeProvider object calls its internal interface.
Next, implement a class based on 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( "The input expiration time (TimeSpan) is invalid " );
            }
            this.ExpirationTime = expirationTimeSpan;
        }
    }

    public override ICallHandler CreateHandler(IUnityContainer container)
    {
        return new CachingCallHandler(this.ExpirationTime) { Order = this.Order };
    }
}
View Code

Next, we complete the injection of AOP in Global.asax:

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

Finally used:

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

The Invoke method is called when the TimeProvider calls the GetCurrentTime method in the period. Invoke method parameter GetNextHandlerDelegate class variable in Invoke call represents the real call GetCurrentTime method. In fact, we can do this:

public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
    
    var retvalue = getNext()(input, getNext);
    if(retvalue.Exception!=null)
    {
        Console.WriteLine("error");
    }
    return retvalue;
}
View Code

This simple implementation is perfectly fine.

It is recommended to look at Microsoft's official information https://msdn.microsoft.com/en-us/library/ff647202.aspx
Code address: Link: https://pan.baidu.com/s/1q98_Otwt1YC_00z_xcavIA Password: b9pj

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326043794&siteId=291194637