ASP.NET MVC Implementation of Dependency Injection

Posted by Wynder on Tue, 10 Sep 2019 13:58:47 +0200

There is automatic injection function in java spring, which makes the code more concise and flexible. So I want to transplant this function into c #, and then I will analyze the implementation process step by step.

1. Scenario analysis using automatic injection

In asp.net mvc, no matter what code logic layers, the final performance layer is the Controller layer, so our injection point is in Controller, where we need to replace the default Controller Factory, scan the code to mark the object to be injected, and instantiate injection.

 public class FastControllerFactory : DefaultControllerFactory
    {
        public override IController CreateController(RequestContext requestContext, string controllerName)
        {
            Type type = this.GetControllerType(requestContext, controllerName);
            Object obj = GetControllerInstance(requestContext, type);

            //Controller Middle marker AutoWired Automatic injection of attributes
            List<FieldInfo> AutoWiredFieldList = type.GetRuntimeFields().Where(f => f.GetCustomAttribute(typeof(AutoWired)) != null).ToList();
            foreach (FieldInfo field in AutoWiredFieldList)
            {
                field.SetValue(obj, InjectUtil.Container.Resolve(field.FieldType));
            }
            return obj as IController;
        }
    }

FastController Factory is our custom Controller factory. It rewrites the CreateController method, assigns the variable marked with AutoWired, takes the instance from the Bean container, and replaces the default factory in the Start method in the Global file.

ControllerBuilder.Current.SetControllerFactory(new FastControllerFactory());

2. Implementation of IOC Container

Custom containers in c # have many mature open source frameworks, such as AutoFac, where we implement a lightweight version ourselves

Source address: https://gitee.com/grassprogramming/FastIOC

Let's focus on how to use asp.net mvc. First, we need to tag the Bean object that needs to be injected. This tag is called Component.

In the Start method in the asp.net mvc Global file, we need to add beans that need to be automatically injected into the container throughout the project.

    public class InjectUtil
    {
        public static ContainerBuilder Container;
        public static void Init()
        {
            Container = new ContainerBuilder();
             //Get all assemblies
            var assemblies = System.Web.Compilation.BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
            //Inject all Component assembly
            Container.RegisterAssemblyTypes(assemblies, typeof(Component),true);
            Container.Build();
        }
    }

 

At this point, the Controller level has been completed, and further processing is needed in the initialization Bean instance method in the IOC container.

       private Object GetInstance(RegisterEntity Entity)
        {
            Object obj = null;
            if (Entity.IsEnableIntercept)
            {
                bool IsExtend = Entity.RealType == Entity.RegistType;
                obj = DynamictProxy.CreateProxyObject(Entity.RealType, Entity.RegistType, Entity.InterceptType, IsExtend, Entity.IsInterceptAllMethod);


            }
            else
            {
                var constructors = Entity.RegistType.GetConstructors();
                obj = constructors[0].Invoke(new Object[] { });
            }
            //Here we use the singleton pattern to instantiate Instance storage,Exposing object instances in advance without subsequent settings
            if (!SingleInstanceDic.ContainsKey(Entity.RealType))
            {
                SingleInstanceDic.Add(Entity.RealType, obj);
            }
        
            //If this class Marked Component,And it's marked. AutoWired Of Field,Automatic injection
            if (Entity.RealType.GetCustomAttribute(typeof(Component), true) != null)
            {
                //Here we will use GetRuntimeFields,This method returns all fields defined on the specified type, including inheritance, non-public, instance, and static fields.
                foreach (FieldInfo Field in Entity.RealType.GetRuntimeFields())
                {
                    if (Field.GetCustomAttribute(typeof(AutoWired), true) != null)
                    {
                        Type FieldType = Field.FieldType;
                        if (Contains(FieldType))
                        {
                            //Determine whether the singleton store contains, and if so, take out the assignment, which prevents dead loops caused by cyclic dependencies
                            if (SingleInstanceDic.ContainsKey(FieldType))
                            {
                                Field.SetValue(obj, SingleInstanceDic[FieldType]);
                            }
                            else
                            {
                                Field.SetValue(obj, Resolve(FieldType));
                            }
                           
                        }
                    }
                }
            }
            return obj;

        }

 

The GetInstance method is the core method of instantiating Bean objects. It's very simple to create objects by reflection. There are two points to note.

1) For a Bean initialization, all variables in the Bean need to be scanned, and if there are nested objects that depend on injection, recursion needs to be used until there is no Field that needs injection.

2) I use the singleton mode here, because there may be dependency injection to B in class A, dependency injection to A in class B, and routine creation process. If we use recursion to scan, we will enter the dead cycle, memory overflow, so we use the singleton of the object, once created, we put it into the dictionary. If the object is scanned again and needs injection, the use is taken out directly, avoiding circular references

3. Other

Dependency injection is required for other classes that are not used in Controller, and they need to be removed directly from the IOC Bean container for use.

 private AuthUtil @AuthUtil = InjectUtil.Container.Resolve<AuthUtil>();

At this point, all the functions are analyzed, and finally an advertisement is made to support a wave of ASP.NET MVC rapid development framework written by myself.

Address: https://gitee.com/grassprogramming/FastExecutor

Topics: ASP.NET Java Spring