.NET Core 中依赖注入框架详解 Autofac

本文将通过演示一个Console应用程序和一个ASP.NET Core Web应用程序来说明依赖注入框架Autofac是如何使用的

Autofac相比.NET Core原生的注入方式提供了强大的功能,详细可以查看Autofac官方API

(1)首先看第一个例子 控制台应用(.NET Core)

通过Nuget安装两个包 => Autofac Autofac.Extensions.DependencyInjection

 public static class Demo
{ 
        public interface IAccount { }
        public interface IMessage { }
        public interface ITool { }
        public class Base : IDisposable
        {
            public Base()
            {
                Console.WriteLine($"{GetType().Name} Created");
            }

            public void Dispose()
            {
                Console.WriteLine($"{GetType().Name} Disposed");
            }
        }
        public class Account : Base, IAccount { }
        public class Message : Base, IMessage { }
        public class Tool : Base, ITool { }

        public static void Run()
        {
            //.NET Core 服务集合
            var serviceCollection = new ServiceCollection()
                .AddTransient<IAccount, Account>()
                .AddTransient<IMessage, Message>();
 
            var containerBuilder = new ContainerBuilder();
            //将服务集合添加到Autofac
            containerBuilder.Populate(serviceCollection);
            //通过类型注册将服务添加到Autofac 
            containerBuilder.RegisterType().As();

            var container =  containerBuilder.Build();
            IServiceProvider provider = new AutofacServiceProvider(container);
            Debug.Assert(provider.GetService() is Account);

        }
}
View Code
  • 可以看到程序中 实例化一个Autofac的ContainerBuilder,通过Populate方法遍历传入的服务集合并添加到Autofac的容器中;
  • RegisterType是通过类型注册将服务添加到Autofac的容器,如果RegisterType和Populate调换加载顺序,则Populate会覆盖RegisterType的服务集合;
  • ContainerBuilder调用Build方法返回的是IContainer对象,该对象实现了ILifetimeScope接口;
  • 在创建AutofacServiceProvider(ILifetimeScope lifetimeScope)返回的是AutofacServiceProvider实现了IServiceProvide,可以和.NET Core 的ServiceProvider一样调用GetService创建服务实例

那么使用Autofac有哪些其它优势?下面看看 属性注入和程序集注入

public static class Demo
    {
        public interface IAccount { }
        public interface IMessage { }
        public interface ITool { }
        public interface ITest { public IMessage Message { get; set; } }

        public class Base 
        {
            public Base()
            {
                Console.WriteLine($"{GetType().Name} Created");
            }

        }
        public class Account : Base, IAccount { }
        public class Message : Base, IMessage { }
        public class Tool : Base, ITool { }
        public class Test : Base, ITest
        {
            public IMessage Message { get; set; }
            public Test(IAccount account, ITool tool)
            {
                Console.WriteLine($"Ctor : Test(IAccount, ITool)");
            }
        }

        public static void Run()
        {
            //.NET Core 服务集合
            //var serviceCollection = new ServiceCollection()
            //    .AddTransient<ITool, Tool>();

            var containerBuilder = new ContainerBuilder();
            //将服务集合添加到Autofac
            //containerBuilder.Populate(serviceCollection);
            //属性注入
            containerBuilder.RegisterType<Test>().As<ITest>().PropertiesAutowired();
            //程序集注入
            containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                .Where(t => t.BaseType == typeof(Base))
                .As(t => t.GetInterfaces()[0])//调用As方法,暴露服务
                .InstancePerLifetimeScope();//每个生命周期作用域的组件在每个嵌套的生命周期作用域中最多只会有一个单一实例


            var container = containerBuilder.Build();
            IServiceProvider provider = new AutofacServiceProvider(container);
            Debug.Assert(provider.GetService<IAccount>() is Account);
            Debug.Assert(provider.GetService<IMessage>() is Message);
            Debug.Assert(provider.GetService<ITool>() is Tool);

            var test = provider.GetService<ITest>();
            Debug.Assert(test.Message is Message);


        }
View Code
  • 属性注入,<code>containerBuilder.RegisterType<Test>().As<ITest>().PropertiesAutowired()</code> 通过PropertiesAutowired 反射方式
  • 程序集注册,获取当前所在程序集Assembly.GetExecutingAssembly(),查找所有基类为Base的Type,调用As暴露服务,InstancePerLifetimeScope 为每个实例创建作用域的生命周期

(2)ASP.NET Core 内置DI 与 Autofac 比较

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            services.AddTransient<IAccount, Account>();
        }
View Code

内置DI 在Startup类 ConfigureServices通过AddTransient方式注册

public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
            .UseServiceProviderFactory(new AutofacServiceProviderFactory());
View Code
Startup.cs 
// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
            //services.AddTransient<IAccount, Account>();//内置DI
        }

        public void ConfigContainer(ContainerBuilder containerBuilder)
        {
            //containerBuilder.RegisterType<Account>().As<IAccount>();
            var assembly = Assembly.GetEntryAssembly();
            containerBuilder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                .Where(t => t.BaseType == typeof(Base))
                .As(t => t.GetInterfaces()[0])
                .InstancePerLifetimeScope();
                
        }
View Code

Autofac 需要在Program.cs中替换 .NET Core的默认容器 UseServiceProviderFactory(new AutofacServiceProviderFactory())

在 Startup.cs 中 创建ConfigContainer方法,服务注册和 控制台应用相似;

真正在实际项目中按照以上方式注册服务会很麻烦,难以维护,可读性差;Autofac提供了一种以模块化方式进行注册

比如我们有不同的业务模块 AModule BModule...,将这些module继承 Autofac.Module 重写Load方法进行服务注册

最后在ConfigContainer中RegisterModule进行模块化注册

public class AModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            var containerBaseType = typeof(ControllerBase);

            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                .Where(t => containerBaseType.IsAssignableFrom(t) && t != containerBaseType)
                .PropertiesAutowired();
        }
    }


public void ConfigContainer(ContainerBuilder containerBuilder)
        {
            
            containerBuilder.RegisterModule<AModule>();
                
        }

 

posted @ 2020-12-11 20:49  待我身高一米八  阅读(972)  评论(0编辑  收藏  举报