Dependency Injection of AutoFac+Actual Mvc, Api and. NET Core

Posted by ialsoagree on Thu, 18 Jul 2019 03:43:22 +0200

Series catalogue

  1. Chapter 1 | Theoretical Basis + Actual Console Programming for AutoFac Injection

  2. Chapter 2 | The Use Skills of AutoFac

  3. Chapter 3 | Actual Asp.Net Framework Web Program Implementing AutoFac Injection

  4. Chapter 4 | Implementation of Dependency Injection with DI in Actual Asp.Net Core

  5. Chapter 5 | Two Ways of Introducing AutoFac into Actual Asp.Net Core

Preface

It was planned to be five articles, but after the first one was published, I found that many people in the. NET environment rejected IoC and DI, making the comment area very lively.
It's interesting that the same thing can be so different in Java and. NET.

So I'll simplify the remaining four articles and synthesize one. It's supposed to be a memo for myself.
GitHub source address: https://github.com/WangRui321/Ray.EssayNotes.AutoFac

Source code is a fictitious project framework, similar to sample code or test program, which contains many annotations, which can help understand DI, or how to implement dependency injection in MVC, WebApi and Core Api respectively.
Therefore, the following content, with the source of better edible effect

Part I: Detailed explanation of AutoFac usage

Noun Interpretation

Old rules, theory first.

Services

A well-defined behavior convention between the provision and consumption components.

Unlike xxxService in the project, AutoFac's services are for containers, which can be simply understood as the type of exposure of components (i.e. the type of service that is open to the outside world) described in the previous chapter, which is what is in the As method:

builder.RegisterType<CallLogger>()
       .As<ILogger>()
       .As<ICallInterceptor>();

Here, for the same CallLogger, the container exposes two services, the ILogger service and the ICallInterceptor service.

Components

A string of code that declares the services it provides and the dependencies it consumes.

It can be understood as the basic unit in a container. There are many components registered in a container. Each component has its own information, such as the exposed service type, life cycle domain, bound object, etc.

LifeTime Scope

  • life cycle

Refers to the length of time a service instance exists in your application: from the beginning of instantiation to the end of release.

  • Scope

It refers to the scope in which it can be shared with other components and consumed in applications. For example, if there is a global static singleton in the application, the scope of the global object instance will be the whole application.

  • Life cycle scope

In fact, the combination of these two concepts can be understood as a unit of work in the application. I'll talk about it in detail later.

How to understand their relationship

Container is a vending machine. Components are the goods on sale inside. Service is the name of the goods on sale.
The process of putting goods (the object of the project) on the shelf of vending machines (containers) is called registration.
When registering, the goods will be labeled with the name of the goods, which is called service.
We can also mark the applicable population and expiration time of this commodity (life cycle scope).
When the packaged product is put into the vending machine, it becomes a merchandise (component).
When a customer needs a product, he only needs to report a product name (service name) to the vending machine. The vending machine finds the corresponding product and throws it to the customer. The process of throwing it to you is called injecting you.
And this vending machine is smart. Before throwing it out, you can also judge whether the product is out of date or not, and whether it should be thrown to you.

Registration component

That is, when the container is initialized, the operation of adding objects to the container. AutoFac encapsulates the following convenient registration methods:

Reflective registration

Specify the injection object and exposure type directly, using RegisterType() or RegisterType (typeof (T) methods:

builder.RegisterType<StudentRepository>()
    .As<IStudentRepository>();
builder.RegisterType(typeof(StudentService))
    .As(typeof(IStudentService));

Instance registration

Register instances into containers using the RegisterInstance() method, usually in two ways:

  • new registers an object:
var output = new StringWriter();
builder.RegisterInstance(output).As<TextWriter>();
  • There are already singletons of registered projects:
builder.RegisterInstance(MySingleton.Instance).ExternallyOwned();

Lambda expression registration

builder.Register(x => new StudentRepository())
    .As<IStudentRepository>();
builder.Register(x => new StudentService(x.Resolve<IStudentRepository>()))
    .As<IStudentService>();

Ramda registration can be used to achieve some operations that conventional reflection can not achieve, such as registration of some complex parameters.

Generic registration

The most common is the registration of generic warehouses:

builder.RegisterGeneric(typeof(BaseRepository<>))
    .As(typeof(IBaseRepository<>))
    .InstancePerLifetimeScope();

Conditional registration

By adding judgment conditions, we can decide whether to execute this registration statement.

  • IfNotRegistered

Representation: If xxx has not been registered, execute the statement:

builder.RegisterType<TeacherRepository>()
    .AsSelf()
    .IfNotRegistered(typeof(ITeacherRepository));

This registration statement is executed only if the ITeacher Repository service type has not been registered.

  • OnlyIf

Representation: Only... Will the statement be executed:

builder.RegisterType<TeacherService>()
    .AsSelf()
    .As<ITeacherService>()
    .OnlyIf(x => 
            x.IsRegistered(new TypedService(typeof(ITeacherRepository)))||
            x.IsRegistered(new TypedService(typeof(TeacherRepository))));

Only when the ITeacher Repository service type or Teacher Repository service type is registered will the registration statement be executed.

Assembly batch registration

One of the most common and practical registration methods is to understand the knowledge of point reflection.

        /// <summary>
        /// Batch registration through reflection assemblies
        /// </summary>
        /// <param name="builder"></param>
        public static void BuildContainerFunc8(ContainerBuilder builder)
        {
            Assembly[] assemblies = Helpers.ReflectionHelper.GetAllAssemblies();

            builder.RegisterAssemblyTypes(assemblies)//All concrete classes in an assembly
                .Where(cc =>cc.Name.EndsWith("Repository")|//screen
                            cc.Name.EndsWith("Service"))
                .PublicOnly()//As long as public access permissions are granted
                .Where(cc=>cc.IsClass)//As long as the class type (mainly for exclusion values and interface types)
                //Except < Teacher Repository >()// / Exclude a Type
                //As (x=> X. GetInterfaces ()[0])// Reflects the interface it implements, exposed by default as the first interface type
                .AsImplementedInterfaces();//Automatically expose all interface types it implements (including IDisposable interfaces)

            builder.RegisterGeneric(typeof(BaseRepository<>))
                .As(typeof(IBaseRepository<>));
        }

As mentioned above, all Repositories and Service s in the project will be registered in batches.

Attribute injection

Before we talk about attribute injection, we need to look at structure injection first.

  • Tectonic injection
    When analysing, constructor injection is used as follows:
    /// <summary>
    /// Student Logic Processing
    /// </summary>
    public class StudentService : IStudentService
    {
        private readonly IStudentRepository _studentRepository;
        /// <summary>
        /// tectonic injection
        /// </summary>
        /// <param name="studentRepository"></param>
        public StudentService(IStudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
        }
    }

When AutoFac parses the type of service written directly in the parameters of the constructor, it searches the existing components inside the container and injects the matching objects into the constructor.

  • Attribute injection
    Attribute injection is different from construct injection in that it injects the corresponding components in the container directly into the attributes in the class in the following form:
    /// <summary>
    /// Teachers'Logical Processing
    /// </summary>
    public class TeacherService : ITeacherService
    {
        /// <summary>
        /// For attribute injection
        /// </summary>
        public ITeacherRepository TeacherRepository { get; set; }

        public string GetTeacherName(long id)
        {
            return TeacherRepository?.Get(111).Name;
        }
    }

To use this property injection, additional annotations using the Properties Autowired () method are required when registering the class to which the property belongs, as follows:

builder.RegisterType<TeacherService>().PropertiesAutowired();

In this way, when the container parses and instantiates the TeacherService class, it maps the components in the container to the attributes in the class, and automatically injects the components into the attributes in the class if they are the same.

  • Be careful

Attribute injection is controversial. Many people call it an anti-pattern, and it is true.
Using attribute injection can make code readability extremely complex (and complex and difficult code must not be good code, no matter how big the technology is).
But attribute injection is not all bad, because attribute injection has a feature:
When constructing injection, if one of the constructor's parameters does not exist in the container, the parsing will report an error.
However, attribute injection is different. When there is no component corresponding to the attribute type in the container, parsing will not report an exception, but will only keep the attribute null.
Using this feature, some special operations can be realized.

Exposure services

That is, the As() function mentioned above, AutoFac provides three ways to annotate the types of exposed services:

Exposure of services by its own type

Using the AsSelf() method to identify exposures of its own type is also the default option when exposed services are not labeled.
The following four ways of writing are equivalent:

builder.RegisterType<StudentService>();//No annotation, default to expose services of their own type
builder.RegisterType<StudentService>().AsSelf();
builder.RegisterType<StudentService>().As<StudentService>();
builder.RegisterType<StudentService>().As(typeof(StudentService));

Exposing Services with Interfaces Implemented

The exposed types can be multiple, such as the CallLogger class implements the ILogger interface and the ICallInterceptor interface, which can be written as follows:

builder.RegisterType<CallLogger>()
       .As<ILogger>()
       .As<ICallInterceptor>()
       .AsSelf();

Specify Exposure Types for Batch Registration of Assemblies

  • Method 1: Designate by yourself
        public static void BuildContainerFunc8(ContainerBuilder builder)
        {
            Assembly[] assemblies = Helpers.ReflectionHelper.GetAllAssemblies();

            builder.RegisterAssemblyTypes(assemblies)//All concrete classes in an assembly
                .Where(cc =>cc.Name.EndsWith("Repository")|//screen
                            cc.Name.EndsWith("Service"))
                .As(x=>x.GetInterfaces()[0])//Reflects the interface it implements and specifies that the first interface type it implements is exposed
        }
  • Method 2: Expose all interface types implemented with it

Using the AsImplemented Interfaces () function, it is equivalent to a class that implements several interface s, which exposes several services, and is equivalent to the function of linking multiple As() above.

        public static void BuildContainerFunc8(ContainerBuilder builder)
        {
            Assembly[] assemblies = Helpers.ReflectionHelper.GetAllAssemblies();

            builder.RegisterAssemblyTypes(assemblies)//All concrete classes in an assembly
                .Where(cc =>cc.Name.EndsWith("Repository")|//screen
                            cc.Name.EndsWith("Service"))
                .AsImplementedInterfaces();//Automatically expose all interface types it implements (including IDisposable interfaces)
        }

Life cycle scope

Equivalent to the concept of UnitWork. The life cycle scopes of AutoFac and. NET Core are listed below and compared.

Life Cycle Scope of AutoFac

Here are several life cycle scopes defined by AutoFac. Some people mentioned in the last comment that life cycle scopes are not very well understood, so I have written an example program for each of the following types. These example programs are very helpful for understanding. As long as you can read these examples, you must be able to understand them. Understand these life cycle scopes. (Examples of project source code are available, you can try to run under the actual, more understandable)

Instance Per Dependency

It is also called each dependency on an instance.
That is to say, every time a new object is taken out of the container, which is equivalent to one new object every time.
Other containers are also marked'Transient'(instantaneous) or'Factory' (factory).

  • register

Use the InstancePerDependency() method for annotation, which is also the default option if not. The following two registration methods are equivalent:

//Not specified, default is instantaneous
builder.RegisterType<Model.StudentEntity>();

//Specify its life cycle domain as instantaneous
builder.RegisterType<Model.StudentEntity>().InstancePerDependency();
  • Analysis:
using (var scope = Container.Instance.BeginLifetimeScope())
{
    var stu1 = scope.Resolve<Model.StudentEntity>();
    Console.WriteLine($"First printing:{stu1.Name}");
    stu1.Name = "Zhang San";
    Console.WriteLine($"Second printing:{stu1.Name}");

    var stu2 = scope.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Second printing:{stu2.Name}");
}

Two instances were parsed above, stu1 and stu2 pointing to two different blocks of memory, which had nothing to do with each other.
Print results:

Single Instance

That is, there is only one instance globally, and within the root container and all nested scopes, each parse returns the same instance.

  • register

Use the SingleInstance() method to identify:

builder.RegisterType<Model.StudentEntity>().SingleInstance();
  • Analysis:
//Resolve directly from the root domain (which can be used in singletons, but is not recommended in others)
var stu1 = Container.Instance.Resolve<Model.StudentEntity>();
stu1.Name = "Zhang San";
Console.WriteLine($"First printing:{stu1.Name}");

using (var scope1 = Container.Instance.BeginLifetimeScope())
{
    var stu2 = scope1.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Second printing:{stu2.Name}");

    stu1.Name = "Li Si";
}
using (var scope2 = Container.Instance.BeginLifetimeScope())
{
    var stu3 = scope2.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Third printing:{stu3.Name}");
}

The stu1, stu2 and stu3 above are all the same instances, pointing to the same memory block in memory.
Print results:

Instance Per Lifetime Scope

That is to say, it is singleton in each life cycle domain.

  • register
    Use the InstancePerLifetimeScope() method to identify:
x.RegisterType<Model.StudentEntity>().InstancePerLifetimeScope();
  • analysis
//Subdomain 1
using (var scope1 = Container.Instance.BeginLifetimeScope())
{
    var stu1 = scope1.Resolve<Model.StudentEntity>();
    Console.WriteLine($"First printing:{stu1.Name}");
    
    stu1.Name = "Zhang San";

    var stu2 = scope1.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Second printing:{stu2.Name}");
}
//Subdomain 2
using (var scope2 = Container.Instance.BeginLifetimeScope())
{
    var stuA = scope2.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Third printing:{stuA.Name}");
    
    stuA.Name = "Li Si";

    var stuB = scope2.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Print 4:{stuB.Name}");
}

As mentioned above, in subdomain 1, although parsed twice, the two parses are the same instance, that is, stu1 and stu2 point to the same memory block I.
Subdomain 2 is the same, stuA and stuB point to the same memory block II, but memory block I and memory block II are not the same.
The printing results are as follows: null for the first and third time:

Instance Per Matching Lifetime Scope

That is, an instance of each matched lifecycle scope.
This domain type is actually one of the above "intra-domain singletons". Unlike this, it allows us to "label" the domain as long as it is singleton in this particular label domain.

  • register
    Register using the Instance Per Matching Lifetime Scope (string tagName) method:
builder.RegisterType<Worker>().InstancePerMatchingLifetimeScope("myRequest");
  • analysis
//myScope tag subdomain 1
using (var myScope1 = Container.Instance.BeginLifetimeScope("myRequest"))
{
    var stu1 = myScope1.Resolve<Model.StudentEntity>();
    stu1.Name = "Zhang San";
    Console.WriteLine($"First printing:{stu1.Name}");

    var stu2 = myScope1.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Second printing:{stu2.Name}");
    //Parse twice, but both times are the same instance (stu1 and stu2 point to the same memory block I)
}
//myScope Label Subdomain 2
using (var myScope2 = Container.Instance.BeginLifetimeScope("myRequest"))
{
    var stuA = myScope2.Resolve<Model.StudentEntity>();
    Console.WriteLine($"Third printing:{stuA.Name}");
    //Because the tag domain has been registered, it can be parsed successfully.
    //But because it is not the same subdomain as above, the parsed instance stuA is not the same instance as before, pointing to another memory block II.
}
//Labelless subdomain 3
using (var noTagScope = Container.Instance.BeginLifetimeScope())
{
    try
    {
        var stuOne = noTagScope.Resolve<Model.StudentEntity>();//Report anomalies
        Console.WriteLine($"Fourth normal printing:{stuOne.Name}");
    }
    catch (Exception e)
    {
        Console.WriteLine($"The fourth abnormal printing:{e.Message}");
    }
    //Because StudentEntity is registered only in the domain with myScope tags, it cannot be resolved here and an exception is reported.
}

Print results:

Attention should be paid to:

  • The third print is null. Different subdomains are different even though they have the same label, so they are not the same instance.
  • Parsing in other labeled domains (including unlabeled domains) will report exceptions

Instance Per Request

This type is suitable for applications of the "request" type, such as MVC and WebApi.
In essence, it is a special case of the previous "singleton in specified domain": there is a string constant in AutoFac called Autofac. Core. Lifetime. MatchingScopeLifetime Tags. Request Lifetime ScopeTag, whose value is "Autofac Web Request", when the label of "singleton in specified domain" is this constant, it is that.“ Every request is singleton.

  • register
    Use the InstancePerRequest() method to annotate:
builder.RegisterType<Model.StudentEntity>().InstancePerRequest();

You can also use the registration law of the above domain singletons (but it is not recommended):

//Use constant markers
builder.RegisterType<Model.StudentEntity>().InstancePerMatchingLifetimeScope(Autofac.Core.Lifetime.MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
//Or write the string directly
builder.RegisterType<Model.StudentEntity>().InstancePerMatchingLifetimeScope("AutofacWebRequest");

The best example is DBContext in EF. We only need one instance of database in a request, even if we use multiple services and multiple repositories, so that we can reduce the number of instances. The consumption of database instance initialization can also realize the function of transaction.

Service lifetimes of. NET Core

Compared with the richness and complexity of AutoFac, the. NET Core is simpler and rougher, with only three types:

Transient

Like the Instance Per Dependency of AutoFac, each is a brand new instance.
Register with AddTransient():

services.AddTransient<IStudentService, StudentService>();

Singleton in Request (Scoped)

It has the same meaning as Instance Per Request of AutoFac, but if AutoFac is actually used in. NET Core, it should be replaced by Instance Per Lifetime Scope of AutoFac.
The reason is that the DI (Microsoft. Extensions. Dependency Injection) that comes with the. NET Core framework takes over the creation of requests and lifecycle scopes entirely, so AutoFac can't be controlled, but using Instance Per Lifetime Scope can achieve the same effect.
Register with AddScoped():

services.AddScoped<IStudentService, StudentService>();

Singleton

Same as AutoFac's Single Instance.
Use AddSingleton(); register:

services.AddSingleton<StudentEntity>();

Part 2: AutoFac injection of. NET Framework Web program

MVC Project

MVC container

In addition to the AutoFac master package, you also need Nuget to import the AutoFac.Mvc5 package:

Container code:

using System;
using System.Linq;
using System.Reflection;
//
using Autofac;
using Autofac.Integration.Mvc;
//
using Ray.EssayNotes.AutoFac.Repository.IRepository;
using Ray.EssayNotes.AutoFac.Repository.Repository;


namespace Ray.EssayNotes.AutoFac.Infrastructure.Ioc
{
    /// <summary>
    /// net framework MVC Program Container
    /// </summary>
    public static class MvcContainer
    {
        public static IContainer Instance;

        /// <summary>
        /// Initialization container
        /// </summary>
        /// <param name="func"></param>
        /// <returns></returns>
        public static void Init(Func<ContainerBuilder, ContainerBuilder> func = null)
        {
            //New container builder for registering components and services
            var builder = new ContainerBuilder();
            //Registration component
            MyBuild(builder); 
            func?.Invoke(builder);
            //Creating containers with builders
            Instance = builder.Build();

            //Set AutoFac to System DI Parser
            System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(Instance));
        }

        public static void MyBuild(ContainerBuilder builder)
        {
            Assembly[] assemblies = Helpers.ReflectionHelper.GetAllAssembliesWeb();

            //Registered Warehouse & Service
            builder.RegisterAssemblyTypes(assemblies)//All concrete classes in an assembly
                .Where(cc => cc.Name.EndsWith("Repository") |//screen
                             cc.Name.EndsWith("Service"))
                .PublicOnly()//As long as public access permissions are granted
                .Where(cc => cc.IsClass)//As long as the class type (mainly for exclusion values and interface types)
                .AsImplementedInterfaces();//Automatically expose all interface types it implements (including IDisposable interfaces)

            //Registered generic warehousing
            builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>));

            //Register Controller
            //Method 1: Register according to reflex
            //builder.RegisterAssemblyTypes(assemblies)
            //    .Where(cc => cc.Name.EndsWith("Controller"))
            //    .AsSelf();
            //Method 2: Extension method for registering MvcController provided by AutoFac
            Assembly mvcAssembly = assemblies.FirstOrDefault(x => x.FullName.Contains(".NetFrameworkMvc"));
            builder.RegisterControllers(mvcAssembly);
        }
    }
}

Project Main Procedures:

  • Global.asax Startup Item

Initialize the container at startup:

using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
//
using Ray.EssayNotes.AutoFac.Infrastructure.Ioc;


namespace Ray.EssayNotes.AutoFac.NetFrameworkMvc
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Initialization container
            MvcContainer.Init();
        }
    }
}
  • Student Controller:

Direct use of structural injection will do:

using System.Web.Mvc;
//
using Ray.EssayNotes.AutoFac.Service.IService;


namespace Ray.EssayNotes.AutoFac.NetFrameworkMvc.Controllers
{
    /// <summary>
    /// Student Api
    /// </summary>
    public class StudentController : Controller
    {
        private readonly IStudentService _studentService;

        /// <summary>
        /// tectonic injection
        /// </summary>
        /// <param name="studentService"></param>
        public StudentController(IStudentService studentService)
        {
            _studentService = studentService;
        }

        /// <summary>
        /// Get the name of the student
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public string GetStuNameById(long id)
        {
            return _studentService.GetStuName(id);
        }
    }
}

Visit

WebApi Project

Api container

In addition to the AutoFac master package, you also need Nuget to import the AutoFac.Api2 package:

Container code:

using System;
using System.Linq;
using System.Reflection;
//
using Autofac;
using Autofac.Integration.WebApi;
//
using Ray.EssayNotes.AutoFac.Repository.Repository;
using Ray.EssayNotes.AutoFac.Repository.IRepository;


namespace Ray.EssayNotes.AutoFac.Infrastructure.Ioc
{
    /// <summary>
    /// NET Framework WebApi Container
    /// </summary>
    public static class ApiContainer
    {
        public static IContainer Instance;

        /// <summary>
        /// Initialization container
        /// </summary>
        /// <param name="config"></param>
        /// <param name="func"></param>
        public static void Init(System.Web.Http.HttpConfiguration config,Func<ContainerBuilder, ContainerBuilder> func = null)
        {
            //New container builder for registering components and services
            var builder = new ContainerBuilder();
            //Registration component
            MyBuild(builder);
            func?.Invoke(builder);
            //Creating containers with builders
            Instance = builder.Build();

            //Set AutoFac parser to system parser
            config.DependencyResolver = new AutofacWebApiDependencyResolver(Instance);
        }

        public static void MyBuild(ContainerBuilder builder)
        {
            var assemblies = Helpers.ReflectionHelper.GetAllAssembliesWeb();

            //Registered Warehouse & Service
            builder.RegisterAssemblyTypes(assemblies)//All concrete classes in an assembly
                .Where(cc => cc.Name.EndsWith("Repository") |//screen
                             cc.Name.EndsWith("Service"))
                .PublicOnly()//As long as public access permissions are granted
                .Where(cc => cc.IsClass)//As long as the class type (mainly for exclusion values and interface types)
                .AsImplementedInterfaces();//Automatically expose all interface types it implements (including IDisposable interfaces)

            //Registered generic warehousing
            builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>));

            //Register ApiController
            //Method 1: Register according to reflex
            //Assembly[] controllerAssemblies = assemblies.Where(x => x.FullName.Contains(".NetFrameworkApi")).ToArray();
            //builder.RegisterAssemblyTypes(controllerAssemblies)
            //    .Where(cc => cc.Name.EndsWith("Controller"))
            //    .AsSelf();
            //Method 2: Extension method for registering ApiController provided by AutoFac
            Assembly mvcAssembly = assemblies.FirstOrDefault(x => x.FullName.Contains(".NetFrameworkApi"));
            builder.RegisterApiControllers(mvcAssembly);
        }
    }
}

WebApi main program

  • Global.asax Startup Item

Initialize the container at project startup:

using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
//
using Ray.EssayNotes.AutoFac.Infrastructure.Ioc;


namespace Ray.EssayNotes.AutoFac.NetFrameworkApi
{
    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Get HttpConfiguration
            HttpConfiguration config = GlobalConfiguration.Configuration;
            //Input Initialization Container
            ApiContainer.Init(config);
        }
    }
}
  • Student Controller:

Constructor injection can be used directly:

using System.Web.Http;
//
using Ray.EssayNotes.AutoFac.Service.IService;


namespace Ray.EssayNotes.AutoFac.NetFrameworkApi.Controllers
{
    /// <summary>
    /// Student Api
    /// </summary>
    public class StudentController : ApiController
    {
        private readonly IStudentService _studentService;

        /// <summary>
        /// tectonic injection
        /// </summary>
        /// <param name="studentService"></param>
        public StudentController(IStudentService studentService)
        {
            _studentService = studentService;
        }

        /// <summary>
        /// Get the name of the student
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet]
        [Route("Student/GetStuNameById")]
        public string GetStuNameById(long id)
        {
            return _studentService.GetStuName(123);
        }
    }
}
  • Running interface

Part Three: DI of. NET Core

DI

Unlike the. NET Framework, the. NET Core puts DI in a very important position, and its framework itself integrates a set of DI containers.
For its own DI, we mainly understand two objects, IServiceCollection and IServiceProvider.

  • IServiceCollection

Used to register services with containers can be analogous to AutoFac's Container Builder.

  • IServiceProvider

It is responsible for providing external examples from the container, which can be analogous to the concepts of AutoFac's analysis.

The registration place is in the startup class under the main program.

But its own registration grammar is not as rich as AutoFac, generic registration and batch registration are all absent, only the following is the most basic form of registration:

using System.Linq;
using System.Reflection;
//
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
//
using Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc.Extensions;
using Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc.Helpers;


namespace Ray.EssayNotes.AutoFac.CoreApi
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            //register
            //Custom registration
            //Registered Warehousing
            services.AddScoped<ITeacherRepository, TeacherRepository>();
            services.AddScoped<IStudentRepository, StudentRepository>();
            services.AddScoped<IBaseRepository<StudentEntity>, BaseRepository<StudentEntity>>();
            services.AddScoped<IBaseRepository<TeacherEntity>, BaseRepository<TeacherEntity>>();
            services.AddScoped<IBaseRepository<BookEntity>, BaseRepository<BookEntity>>();
            //Registration Service
            services.AddScoped<IStudentService, StudentService>();
            services.AddScoped<ITeacherService, TeacherService>();
            services.AddScoped<IBookService, BookService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

So, you will usually expand these registration methods to achieve some of the same convenient registration operations as AutoFac. Here I wrote a small extension based on reflection, which is relatively simple and scribbled. You can refer to the following:

Extension code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
//
using Microsoft.Extensions.DependencyInjection;
//
using Ray.EssayNotes.AutoFac.Model;
using Ray.EssayNotes.AutoFac.Repository.IRepository;
using Ray.EssayNotes.AutoFac.Repository.Repository;
using Ray.EssayNotes.AutoFac.Service.IService;
using Ray.EssayNotes.AutoFac.Service.Service;


namespace Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc.Extensions
{
    /// <summary>
    /// asp.net core registration extension
    /// </summary>
    public static class RegisterExtension
    {
        /// <summary>
        /// Reflective batch registration
        /// </summary>
        /// <param name="services"></param>
        /// <param name="assembly"></param>
        /// <param name="serviceLifetime"></param>
        /// <returns></returns>
        public static IServiceCollection AddAssemblyServices(this IServiceCollection services, Assembly assembly, ServiceLifetime serviceLifetime = ServiceLifetime.Scoped)
        {
            var typeList = new List<Type>();//All sets of classes eligible for registration

            //Screening eligible classes under the current assembly
            List<Type> types = assembly.GetTypes().
                Where(t => t.IsClass && !t.IsGenericType)//Generic classes are excluded
                .ToList();

            typeList.AddRange(types);
            if (!typeList.Any()) return services;

            var typeDic = new Dictionary<Type, Type[]>(); //Pending collection < class, interface >
            foreach (var type in typeList)
            {
                var interfaces = type.GetInterfaces();   //Access interface
                typeDic.Add(type, interfaces);
            }

            //Loop implementation class
            foreach (var instanceType in typeDic.Keys)
            {
                Type[] interfaceTypeList = typeDic[instanceType];
                if (interfaceTypeList == null)//If the class does not implement an interface, it is registered with its own type
                {
                    services.AddServiceWithLifeScoped(null, instanceType, serviceLifetime);
                }
                else//If the class has an implementation interface, it loops its implementation interface and registers one by one.
                {
                    foreach (var interfaceType in interfaceTypeList)
                    {
                        services.AddServiceWithLifeScoped(interfaceType, instanceType, serviceLifetime);
                    }
                }
            }
            return services;
        }

        /// <summary>
        /// Empty registration of exposure types
        /// (If the exposure type is null, it is automatically registered with its own type)
        /// </summary>
        /// <param name="services"></param>
        /// <param name="interfaceType"></param>
        /// <param name="instanceType"></param>
        /// <param name="serviceLifetime"></param>
        private static void AddServiceWithLifeScoped(this IServiceCollection services, Type interfaceType, Type instanceType, ServiceLifetime serviceLifetime)
        {
            switch (serviceLifetime)
            {
                case ServiceLifetime.Scoped:
                    if (interfaceType == null) services.AddScoped(instanceType);
                    else services.AddScoped(interfaceType, instanceType);
                    break;
                case ServiceLifetime.Singleton:
                    if (interfaceType == null) services.AddSingleton(instanceType);
                    else services.AddSingleton(interfaceType, instanceType);
                    break;
                case ServiceLifetime.Transient:
                    if (interfaceType == null) services.AddTransient(instanceType);
                    else services.AddTransient(interfaceType, instanceType);
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(serviceLifetime), serviceLifetime, null);
            }
        }
    }
}

With this extension, we can register in startup with a grammar similar to AutoFac.
Code:

using System.Linq;
using System.Reflection;
//
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
//
using Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc.Extensions;
using Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc.Helpers;


namespace Ray.EssayNotes.AutoFac.CoreApi
{
    public class Startup
    {
        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            //register
            
            //Custom batch registration
            Assembly[] assemblies = ReflectionHelper.GetAllAssembliesCoreWeb();
            //Register repository
            Assembly repositoryAssemblies = assemblies.FirstOrDefault(x => x.FullName.Contains(".Repository"));
            services.AddAssemblyServices(repositoryAssemblies);
            //Registration service  
            Assembly serviceAssemblies = assemblies.FirstOrDefault(x => x.FullName.Contains(".Service"));
            services.AddAssemblyServices(serviceAssemblies);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

Actually, AutoFac has integrated a set of registration extensions for. NET Core. We can introduce AutoFac into. NET Core in two ways: one is to use AutoFac container as an auxiliary container and coexist with. NET Core's DI. We can register components in two containers at the same time; the other is to let AutoFac container take over. NET Core. DI, when registering, only choose to register in Autofac container.
The following two ways of introducing AutoFac are implemented separately.

AutoFac as Auxiliary Registration

Firstly, according to the method of writing AutoFac container before, a new AutoFac container for Core is built.

using System;
//
using Microsoft.Extensions.DependencyInjection;
//
using Autofac;
using Autofac.Extensions.DependencyInjection;
//
using Ray.EssayNotes.AutoFac.Repository.IRepository;
using Ray.EssayNotes.AutoFac.Repository.Repository;


namespace Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc
{
    /// <summary>
    /// Core's AutoFac container
    /// </summary>
    public static class CoreContainer
    {
        /// <summary>
        /// Container example
        /// </summary>
        public static IContainer Instance;

        /// <summary>
        /// Initialization container
        /// </summary>
        /// <param name="services"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        public static IServiceProvider Init(IServiceCollection services, Func<ContainerBuilder, ContainerBuilder> func = null)
        {
            //New container builder for registering components and services
            var builder = new ContainerBuilder();
            //Migrate Core services from DI containers to AutoFac containers
            builder.Populate(services);
            //Custom Registration Component
            MyBuild(builder);
            func?.Invoke(builder);
            //Creating containers with builders
            Instance = builder.Build();

            return new AutofacServiceProvider(Instance);
        }

        /// <summary>
        /// Custom registration
        /// </summary>
        /// <param name="builder"></param>
        public static void MyBuild(this ContainerBuilder builder)
        {
            var assemblies = Helpers.ReflectionHelper.GetAllAssembliesCoreWeb();

            //Registered Warehouse & Service
            builder.RegisterAssemblyTypes(assemblies)
                .Where(cc => cc.Name.EndsWith("Repository") |//screen
                             cc.Name.EndsWith("Service"))
                .PublicOnly()//As long as public access permissions are granted
                .Where(cc => cc.IsClass)//As long as the class type (mainly for exclusion values and interface types)
                .AsImplementedInterfaces();//Automatically expose all interface types it implements (including IDisposable interfaces)

            //Registered generic warehousing
            builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>));

            /*
            //Register Controller
            Assembly[] controllerAssemblies = assemblies.Where(x => x.FullName.Contains(".CoreApi")).ToArray();
            builder.RegisterAssemblyTypes(controllerAssemblies)
                .Where(cc => cc.Name.EndsWith("Controller"))
                .AsSelf();
                */
        }
    }
}

Create a new StartupWithAutoFac class in the main program for registration.
Startup WithAutoFac code:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
//
using Autofac;
//
using Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc;


namespace Ray.EssayNotes.AutoFac.CoreApi
{
    public class StartupWithAutoFac
    {
        public IConfiguration Configuration { get; }

        public StartupWithAutoFac(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
        }

        /// <summary>
        /// Using this method, AutoFac can be used to assist registration. This method is executed after ConfigureServices(), so when overwriting registration occurs, the latter will prevail.
        /// Stop using builders to create AutoFac containers. The system has taken over.
        /// </summary>
        /// <param name="builder"></param>
        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.MyBuild();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

The rest of the world is the same as the original startup, except for an additional ConfigureContainer() method, in which you can register freely according to AutoFac's grammar.

Then modify the program class to hook AutoFac into the pipeline and designate the StartupWithAutoFac class as the registration entry:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;


namespace Ray.EssayNotes.AutoFac.CoreApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                //The first is to use self-contained DI
                //.UseStartup<Startup>();

                //Second: Adding AutoFac as an Auxiliary Container
                .HookAutoFacIntoPipeline()
                .UseStartup<StartupWithAutoFac>();

                //Third: Add AutoFac takeover dependency injection
                //.UseStartup<StartupOnlyAutoFac>();
    }
}

AutoFac takes over Registration

The main program creates a new StartupOnlyAutoFac class.
The code is as follows:

using System;
//
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
//
using Ray.EssayNotes.AutoFac.Infrastructure.CoreIoc;


namespace Ray.EssayNotes.AutoFac.CoreApi
{
    public class StartupOnlyAutoFac
    {
        public IConfiguration Configuration { get; }

        public StartupOnlyAutoFac(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public IServiceProvider ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            return CoreContainer.Init(services);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseMvc();
        }
    }
}

Here, the return type of the ConfigureServices() method is changed directly, and then AutoFac is registered directly within the method.

Finally, of course, change the program class to specify the StartupOnlyAutoFac class as the registration entry.
Code:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;


namespace Ray.EssayNotes.AutoFac.CoreApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                //The first is to use self-contained DI
                //.UseStartup<Startup>();

                //Second: Adding AutoFac as an Auxiliary Container
                //.HookAutoFacIntoPipeline()
                //.UseStartup<StartupWithAutoFac>();

                //Third: Add AutoFac takeover dependency injection
                .UseStartup<StartupOnlyAutoFac>();
    }
}

Run call interface:

Topics: PHP Attribute github Database Programming