dotNET Core 3.X uses Autofac to enhance dependency injection

In the last article dotNET Core 3.X Dependent Injection The dependency injection function of the dotNET Core framework itself is briefly described in this article. In most cases, it can be satisfied by using the dependency injection function of the framework. In some special scenarios, we need to introduce a third-party injection framework.

Why use Autofac?

If you have used Dependent Injection in the dotNET Framwork era before, you are no stranger to Autofac. Autofac is also very convenient in dotNET Core because it provides more functionality:

  • Attribute Injection
  • Batch Injection
  • AOP function of dynamic proxy

Using Autofac in dotNET Core

There is a difference between using Autofac in dotNET Core 2.x and 3.x, so the simple use in both versions is described below.

2.x

1. Create a dotNET Core version 2.1 WebAPI project;
2. Create IUserService interface and UserService class

public interface IUserService
{
    string GetUserName();
}
public class UserService: IUserService
{
    public string GetUserName()
    {
        return "oec2003";
    }
}

3. Create a UserController to add dependency injection to the constructor

[Route("api/[controller]/[action]")]
[ApiController]
public class UserController: ControllerBase
{
    private readonly IUserService _userService;

    public UserController(IUserService userService)
    {
        _userService = userService;
    }
    public string GetUserName()
    {
        return _userService.GetUserName();
    }
}

4. Add Autofac.Extensions.DependencyInjection NuGet Reference

5. Modify the ConfigureServices method of the Startup class

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    //Create Autofac Container
    var containerBuilder = new ContainerBuilder();
    containerBuilder.Populate(services);
    //Register the UserService class as an implementation of IUserService
    containerBuilder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
    var container = containerBuilder.Build();
    //Container inside nozzle
    return new AutofacServiceProvider(container);
}

3.x

1. Create dotNET Core 3.x projects and related classes, referring to the steps one to four above;

2. Modify the Program class to use AutofacServiceProviderFactory instead of the factory where the service provider was created:

public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });

3. Modify the Startup class to add the ConfigureContainer method to it. Like the ConfigureServices method, the framework is executed by naming constraints:

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<UserService>().As<IUserService>().InstancePerLifetimeScope();
}

Enhancements to Autofac

All the examples below were completed in dotNET Core version 3.1.

Attribute Injection

Dependent injection of the dotNET Core framework itself only supports constructors and FromSeries, while Autofac supports property injection.

Using property injection is simple, calling the PropertiesAutowired method when registering a type, as follows:

1. Adjust UserController to define IUserService as a property

public class UserController: ControllerBase
{
    public IUserService UserService { get; set; }
    
    public string GetUserName()
    {
        return UserService.GetUserName();
    }
}

2. Modify the ConfigureServices method of the Startup class to add calls to the AddControllersAsServices method

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddControllersAsServices();
}

3. Modify the ConfigureContainer of the Startup class,

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<UserService>().As<IUserService>()
        .InstancePerLifetimeScope();

    var controllerBaseType = typeof(ControllerBase); 
    builder.RegisterAssemblyTypes(typeof(Program).Assembly)
        .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
        .PropertiesAutowired();
}
  • Add a call to the AddController sAsServices method in the ConfigureServices method whenever property injection is required in the Controller;
  • The PropertiesAutowired method is added to the type of injection that uses properties, such as the code above that uses properties in Controller, so PropertiesAutowired is added after all Controller registrations;
  • If the UserService class references IDeptService as an attribute, register it as follows:
public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<DeptService>().As<IDeptService>()
        .InstancePerLifetimeScope();
    builder.RegisterType<UserService>().As<IUserService>()
        .PropertiesAutowired()
        .InstancePerLifetimeScope();
}

Bulk Registration

In fact, the above code already involves batch registration, that is, registration of all ontrollers:

var controllerBaseType = typeof(ControllerBase); 
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
    .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
    .PropertiesAutowired();
  • All Controllers inherit from the base class ControllerBase, and get the type of the base class first.
  • Find all types in the assembly where the Program class resides that implement ControllerBase and register them.

Let's look at another scenario, where the UserService service was created in the example above, and now the DeptService service class is created:

public interface IDeptService
{
    string GetDeptName();
}
public class DeptService:IDeptService
{
    public string GetDeptName()
    {
        return "Product Department";
    }
}

Modify the ConfigureContainer method of the Startup class to implement bulk registration:

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterAssemblyTypes(typeof(Program).Assembly)
        .Where(t => t.Name.EndsWith("Service"))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope();
}

Find all types named Service in the assembly where the Program class is located and register them.More often than not, it's based on the actual situation.

AOP function of dynamic proxy

To use the functionality of the dynamic proxy, you need to reference the NuGet package:Autofac.Extras.DynamicProxy As follows:

The concept of AOP is not covered here, but the difference from dotNET Core's built-in interceptors (Filter, middleware) is that Autofac's AOP is based on business methods rather than HTTP.

1. Create UserServiceInterceptor intercept class, inherited from IInterceptor

public class UserServiceInterceptor:IInterceptor
{
    public virtual void Intercept(IInvocation invocation)
    {
        Console.WriteLine($"{DateTime.Now}: Before method execution");
        invocation.Proceed();
        Console.WriteLine($"{DateTime.Now}: After method execution");
    }
}

2. Modify the ConfigureContainer method in the Startup class to register for AOP

public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterType<UserServiceInterceptor>();
    builder.RegisterType<UserService>().As<IUserService>()
        .EnableInterfaceInterceptors()
        .InstancePerLifetimeScope();
}
  • Register UserServiceInterceptor Interceptor
  • Call EnableInterfaceInterceptors to enable interceptors when registering UserService services

3. Modify the UserService class to add AOP attribute tags

[Intercept(typeof(UserServiceInterceptor))]
public class UserService: IUserService
{
    //public IDeptService DeptService { get; set; }
    public string GetUserName()
    {
        Console.WriteLine($"{DateTime.Now}: Method Execution in Progress");
        return "oec2003";
        //return $"oec2003({DeptService.GetDeptName()})";
    }
}

4. The result of the call is as follows:

summary

This is a throwing brick introduction, Autofac also has many functions that are not currently in use, so they are not included in this article, such as sub-containers.Whether you use the dotNET Core framework's own dependency injection or Autofac depends on the scenario, and of course they can coexist.

Sample code: https://github.com/oec2003/DotNetCoreThreeAPIDemo/tree/master/AutofacNetCore3.1Demo

Keywords: Attribute github

Added by techjosh on Tue, 09 Jun 2020 03:55:52 +0300