ASP. NET MVC Complete process for implementing dependency injection

  • 2021-11-10 09:26:42
  • OfStack

Preface

There is auto-injection in spring of java, which makes the code more concise and flexible, so I want to port this function to c #, and then analyze the implementation process step by step

1. Use automatic injection scenario analysis

In asp. net mvc, no matter what code logic layer, the final presentation layer is Controller layer, so our injection point is in Controller. Here we need to replace the default ControllerFactory, scan the code to mark the object to be injected, and instantiate the 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 Mark in 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;
    }
  }

FastControllerFactory is a custom Controller factory. Rewrite the CreateController method. For the variable marked with the custom annotation AutoWired, take out the instance from the Bean container to assign value. At the same time, we also need to replace the default factory in the Start method in the Global file


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

2. Implementation of IOC container

The custom container in c # has many mature open source frameworks, such as AutoFac, etc. Here we implement a lightweight version by ourselves

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

Here, we will focus on how to use it in asp. net mvc. First, we need to mark the Bean object that needs to be injected. This mark is called Component.

In the Start method in the asp. net mvc Global file, we need to add the Bean that needs automatic injection throughout the project to the container


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 Component 
      Container.RegisterAssemblyTypes(assemblies, typeof(Component),true);
      Container.Build();
    }
  }

At this point, the Controller level has been completed, and the next step is to initialize the 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, the singleton pattern is used to instantiate Instance Storage , Expose object instances without subsequent settings in advance 
      if (!SingleInstanceDic.ContainsKey(Entity.RealType))
      {
        SingleInstanceDic.Add(Entity.RealType, obj);
      }
    
      // If this class Marked Component And it's marked AutoWired Adj. Field For automatic injection 
      if (Entity.RealType.GetCustomAttribute(typeof(Component), true) != null)
      {
        // To use here GetRuntimeFields Returns all fields defined on the specified type, including inherited, 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))
            {
              // Judge whether it is included in singleton storage, and if so, take out the assignment, which can prevent the infinite loop caused by circular dependence 
              if (SingleInstanceDic.ContainsKey(FieldType))
              {
                Field.SetValue(obj, SingleInstanceDic[FieldType]);
              }
              else
              {
                Field.SetValue(obj, Resolve(FieldType));
              }
              
            }
          }
        }
      }
      return obj;

    }

GetInstance method is the core method of instantiating Bean object. In fact, it is very simple, that is, creating objects through reflection. There are two points to pay attention to

1) When an Bean is initialized, it needs to scan all variables in Bean. If there are nested objects with dependency injection inside, it needs to use recursion until there is no Field to be injected

2) I'm using the singleton pattern here, Because dependency injection of B in the A class may exist during testing, In the B class to A dependency injection, normal creation process, if use recursive scanning, will enter an infinite loop, memory overflow, so use the object singleton, 1 once created into the dictionary, if scan again to the object need to inject, then directly take out to use, to avoid circular reference

3. Others

For other classes that are not used in Controller, dependency injection is required, then it needs to be taken out of the Bean container of IOC and used directly


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

Functions here are all analyzed, finally make an advertisement, write their own ASP. NET MVC rapid development framework, I hope to support 1 wave

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

Summarize


Related articles: