主頁 > .NET開發 > Autofac 集成測驗 在 ConfigureContainer 之后進行 Mock 注入

Autofac 集成測驗 在 ConfigureContainer 之后進行 Mock 注入

2021-06-09 14:41:45 .NET開發

在使用 Autofac 框架進行開發后,撰寫集成測驗時,需要用 Mock 的用于測驗的模擬的型別去代替容器里面已注入的實際型別,也就需要在 Autofac 完全收集完成之后,再次注入模擬的物件進行覆寫原有業務代碼注冊的正式物件,但 Autofac 默認沒有提供此機制,我閱讀了 Autofac 的源代碼之后,創建了一些輔助代碼,實作了此功能,本文將告訴大家如何在集成測驗里面,在使用了 Autofac 的專案里面,在所有收集完成之后,注入用于測驗的 Mock 型別,和 Autofac 接入的原理

背景

為什么選擇使用 Autofac 框架?原因是在此前的 WPF 專案里面,有使用過的是 MEF 和 Autofac 兩個框架,而 MEF 的性能比較糟心,解決 MEF 性能問題的是 VS-MEF 框架,在后續開發的一個 ASP.NET Core 專案里面,也就自然選用了 Autofac 框架

對比原生的 ASP.NET Core 自帶的 DI 框架,使用 Autofac 的優勢在于支持模塊化的初始化,支持屬性注入

默認的 Autofac 可以通過 Autofac.Extensions.DependencyInjection 將 Autofac 和 dotnet 通用依賴注入框架合入在一起,但在 Autofac 里面的定制要求是在 Startup 的 ConfigureContainer 函式里面進行依賴注入,也就是在默認的 ASP.NET Core 里面沒有提供更靠后的依賴注入方法,可以在完成收集之后,再次注入測驗所需要的型別,覆寫業務代碼里面的實際物件

需求

假定在一個應用,如 ASP.NET Core 應用里面,進行集成測驗,想要在集成測驗里面,使用專案里面的依賴注入關系,只是將部分型別替換為測驗專案里面的模擬的型別

而在應用里面,實際的業務型別是在 Autofac 的 Module 進行注入的,在應用里面的大概邏輯如下,在 Program 的 CreateHostBuilder 函式里面通過 UseServiceProviderFactory 方法使用 Autofac 替換 原生 的框架

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                })
                // 使用 auto fac 代替默認的 IOC 容器 
                .UseServiceProviderFactory(new AutofacServiceProviderFactory());

在 Startup 里面添加 ConfigureContainer 方法,代碼如下

        public void ConfigureContainer(ContainerBuilder builder)
        {
            
        }

在 ConfigureContainer 里面注入具體的需要初始化的業務模塊,例如 FooModule 模塊,在具體模塊里面注入實際業務的型別,如 Foo 型別,代碼如下

    public class Startup
    {
    	// 忽略代碼

        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterModule(new FooModule());
        }
    }

    class FooModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            Console.WriteLine($"06 FooModule");

            builder.RegisterType<Foo>().As<IFoo>();
        }
    }

    public class Foo : IFoo
    {
    }

    public interface IFoo
    {
    }

現在的需求是在集成測驗里面,將 IFoo 的實際型別從 Foo 替換為 TestFoo 型別

在集成測驗專案里面,可以使用如下代碼獲取實際的專案的依賴注入收集

                var hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
                    })
                    // 使用 auto fac 代替默認的 IOC 容器 
                    .UseServiceProviderFactory(new AutofacServiceProviderFactory());

                var host = hostBuilder.Build();

                var foo = host.Services.GetService<IFoo>();

以上的 foo 就是從收集的容器里面獲取的 IFoo 物件,以上代碼獲取到的是業務代碼的 Foo 型別物件,假定需要讓容器里面的 IFoo 的實際型別作為測驗的 TestFoo 型別,就需要在實際專案的依賴注入收集完成之前,進行測驗的注入

但實際上沒有在 Autofac 里面找到任何的輔助方法可以用來實作此功能,如果是默認的應用框架,可以在 ConfigureWebHostDefaults 函式之后,通過 ConfigureServices 函式覆寫在 Startup 的 ConfigureServices 函式注入的型別

實作方法

實作的方法是很簡單的,關于此實作為什么能解決問題還請參閱下文的原理部分

集成測驗專案不需要改動原有的業務專案即可完成測驗,實作方法是在集成測驗專案里面添加 FakeAutofacServiceProviderFactory 用來替換 Autofac 的 AutofacServiceProviderFactory 型別,代碼如下

    class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        public FakeAutofacServiceProviderFactory(
            ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
            Action<ContainerBuilder>? configurationActionOnBefore = null,
            Action<ContainerBuilder>? configurationActionOnAfter = null)
        {
            _configurationActionOnAfter = configurationActionOnAfter;
            AutofacServiceProviderFactory =
                new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
        }

        private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
        private readonly Action<ContainerBuilder>? _configurationActionOnAfter;

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            return AutofacServiceProviderFactory.CreateBuilder(services);
        }

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            _configurationActionOnAfter?.Invoke(containerBuilder);
            return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
        }
    }

可以看到本質的 FakeAutofacServiceProviderFactory 實作就是通過 AutofacServiceProviderFactory 的屬性實作,只是在 CreateServiceProvider 方法里面加入了委托,可以方便在單元測驗里面進行注入自己的方法

在集成測驗專案里面的使用方法如下

                var hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
                    })
                    // 使用 auto fac 代替默認的 IOC 容器 
                    .UseServiceProviderFactory(new FakeAutofacServiceProviderFactory(configurationActionOnAfter:
                        builder =>
                        {
                            builder.RegisterModule<TestModule>();
                        }));

傳入的委托需要注入測驗的初始化模塊,也就是 TestModule 需要加入注入,通過上面代碼,可以讓 TestModule 在依賴注入的最后進行初始化,在 TestModule 里面加入實際的測驗型別注入的代碼

    class TestModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<TestFoo>().As<IFoo>();
        }
    }

    class TestFoo : IFoo
    {
    }

通過上面方法就可以讓集成測驗專案從容器里面獲取 IFoo 的物件,拿到的是 TestFoo 型別,集成測驗專案的代碼如下

    [TestClass]
    public class FooTest
    {
        [ContractTestCase]
        public void Test()
        {
            "依賴注入的時機,可以在完成收集之后,覆寫原有的型別".Test(() =>
            {
                var hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
                    })
                    // 使用 auto fac 代替默認的 IOC 容器 
                    .UseServiceProviderFactory(new FakeAutofacServiceProviderFactory(configurationActionOnAfter:
                        builder =>
                        {
                            builder.RegisterModule<TestModule>();
                        }));

                var host = hostBuilder.Build();

                var foo = host.Services.GetService<IFoo>();
                Assert.IsInstanceOfType(foo, typeof(TestFoo));
            });
        }
    }

    class TestModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<TestFoo>().As<IFoo>();
        }
    }

    class TestFoo : IFoo
    {
    }

    class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        public FakeAutofacServiceProviderFactory(
            ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
            Action<ContainerBuilder>? configurationActionOnBefore = null,
            Action<ContainerBuilder>? configurationActionOnAfter = null)
        {
            _configurationActionOnAfter = configurationActionOnAfter;
            AutofacServiceProviderFactory =
                new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
        }

        private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
        private readonly Action<ContainerBuilder>? _configurationActionOnAfter;

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            return AutofacServiceProviderFactory.CreateBuilder(services);
        }

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            _configurationActionOnAfter?.Invoke(containerBuilder);
            return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
        }
    }

以上集成測驗使用了 CUnit 中文單元測驗框架輔助,在上面代碼里面,可以看到集成測驗里面的容器拿到的 IFoo 物件就是 TestFoo 型別,通過這個方法就可以在業務代碼執行程序,注入測驗需要的型別

為什么通過以上的代碼即可實作此功能,為什么需要自己實作一個 FakeAutofacServiceProviderFactory 型別,為什么不能在 AutofacServiceProviderFactory.CreateServiceProvider 方法之前注入型別,而是需要再定義一個 TestModule 模塊,在測驗初始化模塊進行初始化,更多細節請看下文

原理

回答以上問題,需要了解各個注入方法呼叫的順序,我在代碼里面通過控制臺輸出各個方法的順序,標記了順序的代碼放在本文最后

以下是按照呼叫順序的方法代碼

Startup 的 ConfigureServices 方法

    public class Startup
    {
        // 忽略代碼

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            Console.WriteLine($"01 ConfigureServices");
        }
    }

在 Startup 的 ConfigureServices 是依賴注入中最開始呼叫的方法,這也是原生的框架自帶的方法

IHostBuilder 的 ConfigureServices 擴展方法

                var hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
                    })
                    .ConfigureServices(collection => Console.WriteLine($"02 ConfigureServices Delegate"))

在 IHostBuilder 的 ConfigureServices 擴展方法將會在 Startup 的 ConfigureServices 方法執行完成之后呼叫,因此如果只使用原生的依賴注入,可以在此方法進行覆寫,也就是如果沒有使用 Autofac 框架,只使用原生的框架,可以在集成測驗,在此方法注入測驗的型別

Startup 的 ConfigureContainer 方法

    public class Startup
    {
        // 忽略代碼

        public void ConfigureContainer(ContainerBuilder builder)
        {
            Console.WriteLine($"03 ConfigureContainer");
            builder.RegisterModule(new FooModule());
        }
    }

可以看到 public void ConfigureContainer(ContainerBuilder builder) 方法的呼叫在 ConfigureServices 方法之后,在 Autofac 也通過此機制實作代替原生的依賴注入功能,也通過此方法從原生的注入獲取依賴

關于 Autofac 的實際邏輯,請參閱下文

FakeAutofacServiceProviderFactory 的 CreateServiceProvider 方法

在如上代碼,咱撰寫了 FakeAutofacServiceProviderFactory 用于替換 AutofacServiceProviderFactory 型別,在 FakeAutofacServiceProviderFactory 的 CreateServiceProvider 方法將會在呼叫 ConfigureContainer 之后執行

    class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        public FakeAutofacServiceProviderFactory(
            ContainerBuildOptions containerBuildOptions = ContainerBuildOptions.None,
            Action<ContainerBuilder>? configurationActionOnBefore = null,
            Action<ContainerBuilder>? configurationActionOnAfter = null)
        {
            _configurationActionOnAfter = configurationActionOnAfter;
            AutofacServiceProviderFactory =
                new AutofacServiceProviderFactory(containerBuildOptions, configurationActionOnBefore);
        }

        private AutofacServiceProviderFactory AutofacServiceProviderFactory { get; }
        private readonly Action<ContainerBuilder>? _configurationActionOnAfter;

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            return AutofacServiceProviderFactory.CreateBuilder(services);
        }

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            Console.WriteLine($"04 FakeAutofacServiceProviderFactory");
            _configurationActionOnAfter?.Invoke(containerBuilder);
            return AutofacServiceProviderFactory.CreateServiceProvider(containerBuilder);
        }
    }

在以上的 CreateServiceProvider 方法將會在 Startup 的 ConfigureContainer 方法之后執行,如上面代碼,按照上面代碼,將會執行 _configurationActionOnAfter 委托

因此下一個執行的就是傳入的委托

                IHostBuilder? hostBuilder = Host.CreateDefaultBuilder()
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>();
                        webBuilder.UseTestServer(); //關鍵是多了這一行建立TestServer
                    })
                    .ConfigureServices(collection => Console.WriteLine($"02 ConfigureServices Delegate"))
                    // 使用 auto fac 代替默認的 IOC 容器 
                    .UseServiceProviderFactory(new FakeAutofacServiceProviderFactory(configurationActionOnAfter:
                        builder =>
                        {
                            Console.WriteLine($"05 ConfigurationActionOnAfter");
                            builder.RegisterModule<TestModule>();
                        }));

也就是如上代碼的 ConfigurationActionOnAfter 委托

但盡管 FakeAutofacServiceProviderFactory 的 CreateServiceProvider 在 Startup 的 ConfigureContainer 方法之后執行,實際上很多開發者不會在 Startup 的 ConfigureContainer 方法完成注冊,而是在 ConfigureContainer 里面初始化模塊,如上面代碼,在業務邏輯注冊的模塊的初始化還沒被呼叫,只有在實際的 ContainerBuilder 呼叫 Build 方法,才會執行模塊的 Load 方法

因此下一個呼叫就是業務邏輯注冊的模塊

FooModule 的 Load 方法

按照 Autofac 的定義,在 ConfigureContainer 的 Build 方法里面,才會執行模塊的初始化,呼叫 Load 方法

    class FooModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            Console.WriteLine($"06 FooModule");

            builder.RegisterType<Foo>().As<IFoo>();
        }
    }

在 Autofac 里面,將會按照模塊注冊的順序,呼叫模塊的 Load 方法,如上面代碼,可以看到 TestModule 在最后被注冊,因此將會最后執行

TestModule 的 Load 方法

在上面代碼 TestModule 是最后注冊到 Autofac 的模塊,也就是 TestModule 將會最后被執行

    class TestModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            Console.WriteLine($"07 TestModule");

            builder.RegisterType<TestFoo>().As<IFoo>();
        }
    }

如上面代碼,在 TestModule 注入的測驗型別,將會替換業務代碼的實際型別

Autofac 接入的方法

通過上面的方法呼叫順序,大家也可以了解為什么集成測驗的代碼這樣寫就有效果,更深入的邏輯是 Autofac 的設計,為什么可以讓 Autofac 框架可以接入到 ASP.NET Core 應用里面,我在此前可一直都是在 WPF 框架使用的,這個問題其實很簡單,所有的 dotnet 專案,無論是 ASP.NET Core 還是 WPF 等,都是相同的 dotnet 邏輯,裝配方式都相同,只是頂層業務邏輯實作方法有所不同,因此只需要加一點適配邏輯就能通用

從上面專案安裝的 NuGet 包可以看到,安裝了 Autofac.Extensions.DependencyInjection 庫就是提供 Autofac 與 dotnet 通用依賴注入框架鏈接的功能,而 ASP.NET Core 原生的框架就是基于 dotnet 通用依賴注入框架,因此就能將 Autofac 接入到 ASP.NET Core 應用

在 UseServiceProviderFactory 方法里面,將會執行 ASP.NET Core 框架的依賴注入容器相關方法,此方法注入的 IServiceProviderFactory 帶泛形的型別,將可以支持在 Startup 方法里面添加 ConfigureContainer 方法,引數就是 IServiceProviderFactory 的泛形

如加入了 FakeAutofacServiceProviderFactory 型別,此型別繼承了 IServiceProviderFactory<ContainerBuilder> 介面,也就是 IServiceProviderFactory 的 泛形 是 ContainerBuilder 型別,因此可以在 Startup 的 ConfigureContainer 方法引數就是 ContainerBuilder 型別

    public class Startup
    {
        // 忽略代碼

        public void ConfigureContainer(ContainerBuilder builder)
        {
          
        }
    }

而 ConfigureContainer 將會被 Microsoft.AspNetCore.Hosting.GenericWebHostBuilder 進行呼叫,在 GenericWebHostBuilder 的呼叫順序是先呼叫 ConfigureServices 再呼叫 對應的 ConfigureContainer 方法

在 Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider 方法就是實際創建容器的方法,這個方法里面,將會先呼叫完成 ConfigureServices 的配置,然后再呼叫 ConfigureContainer 的配置,代碼如下

    public class HostBuilder : IHostBuilder
    {
        private void CreateServiceProvider()
        {
           // 忽略代碼
            var services = new ServiceCollection();

            foreach (Action<HostBuilderContext, IServiceCollection> configureServicesAction in _configureServicesActions)
            {
                configureServicesAction(_hostBuilderContext, services);
            }

            object containerBuilder = _serviceProviderFactory.CreateBuilder(services);

            foreach (IConfigureContainerAdapter containerAction in _configureContainerActions)
            {
                containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
            }

            _appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
        }
    }

此時的 _serviceProviderFactory 將會是注入的 FakeAutofacServiceProviderFactory 型別,將會呼叫對應的 CreateBuilder 方法,也就是如下代碼將會呼叫

    class FakeAutofacServiceProviderFactory : IServiceProviderFactory<ContainerBuilder>
    {
        // 忽略代碼
        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            return AutofacServiceProviderFactory.CreateBuilder(services);
        }
    }

在 HostBuilder 的 _configureContainerActions 委托呼叫 ConfigureContainer 的邏輯,實際就是 Startup 型別里面定義的 ConfigureContainer 方法

因此就是先呼叫 Startup 型別和 IHostBuilder 的 ConfigureServices 方法,然后再呼叫 ConfigureContainer 方法

在 Autofac 的 AutofacServiceProviderFactory 在 CreateBuilder 方法就可以拿到了原生注冊的所有型別,因為在呼叫 CreateBuilder 之前已經完成了所有的原生邏輯

在 AutofacServiceProviderFactory 的 CreateBuilder 方法將會先創建 ContainerBuilder 物件,然后呼叫 Populate 方法,從原生的 IServiceCollection 獲取注冊的型別,重新放到 ContainerBuilder 容器

        public ContainerBuilder CreateBuilder(IServiceCollection services)
        {
            var builder = new ContainerBuilder();

            builder.Populate(services);

            _configurationAction(builder);

            return builder;
        }

上面代碼的 ContainerBuilder 是 Autofac 框架的,而 Populate 是擴展方法,和 AutofacServiceProviderFactory 都是在 Autofac.Extensions.DependencyInjection 庫提供的,通過此擴展方法和 AutofacServiceProviderFactory 即可實作 Autofac 和 dotnet 原生接入,在 Populate 方法從 dotnet 原生拿到注冊的型別,放入到 Autofac 的 ContainerBuilder 里,這樣所有之前使用 dotnet 原生注入的型別就可以在 Autofac 拿到

        public static void Populate(
            this ContainerBuilder builder,
            IEnumerable<ServiceDescriptor> descriptors,
            object lifetimeScopeTagForSingletons = null)
        {
            if (descriptors == null)
            {
                throw new ArgumentNullException(nameof(descriptors));
            }

            builder.RegisterType<AutofacServiceProvider>().As<IServiceProvider>().ExternallyOwned();
            builder.RegisterType<AutofacServiceScopeFactory>().As<IServiceScopeFactory>();

            Register(builder, descriptors, lifetimeScopeTagForSingletons);
        }

以上的 IEnumerable<ServiceDescriptor> 就是 IServiceCollection 型別的物件,實際代碼是 Register 里面拿到注冊的型別,重新放入到 Autofac 里

        private static void Register(
            ContainerBuilder builder,
            IEnumerable<ServiceDescriptor> descriptors,
            object lifetimeScopeTagForSingletons)
        {
            foreach (var descriptor in descriptors)
            {
                if (descriptor.ImplementationType != null)
                {
                    // Test if the an open generic type is being registered
                    var serviceTypeInfo = descriptor.ServiceType.GetTypeInfo();
                    if (serviceTypeInfo.IsGenericTypeDefinition)
                    {
                        builder
                            .RegisterGeneric(descriptor.ImplementationType)
                            .As(descriptor.ServiceType)
                            .ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons);
                    }
                    else
                    {
                        builder
                            .RegisterType(descriptor.ImplementationType)
                            .As(descriptor.ServiceType)
                            .ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons);
                    }
                }
                else if (descriptor.ImplementationFactory != null)
                {
                    var registration = RegistrationBuilder.ForDelegate(descriptor.ServiceType, (context, parameters) =>
                        {
                            var serviceProvider = context.Resolve<IServiceProvider>();
                            return descriptor.ImplementationFactory(serviceProvider);
                        })
                        .ConfigureLifecycle(descriptor.Lifetime, lifetimeScopeTagForSingletons)
                        .CreateRegistration();

                    builder.RegisterComponent(registration);
                }
                else
                {
                    builder
                        .RegisterInstance(descriptor.ImplementationInstance)
                        .As(descriptor.ServiceType)
                        .ConfigureLifecycle(descriptor.Lifetime, null);
                }
            }
        }

上面代碼拿到的 ServiceDescriptor 就是在原生框架里面的注入型別的定義,可以看到這些都重新放到 Autofac 的容器里面

這就是為什么 Autofac 能拿到在 ASP.NET Core 框架里面其他框架注入的型別的代碼

在 HostBuilder 的 CreateServiceProvider 方法最后就是呼叫 IServiceProviderFactory 的 CreateServiceProvider 方法回傳實際的容器

也就是呼叫了 Autofac 的 CreateServiceProvider 方法,代碼如下

        public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder)
        {
            if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder));

            var container = containerBuilder.Build(_containerBuildOptions);

            return new AutofacServiceProvider(container);
        }

可以看到本質就是呼叫了 ContainerBuilder 的 Build 方法,而在 Build 方法里面,才會初始化 Autofac 的模塊,因此在 FakeAutofacServiceProviderFactory 的 CreateServiceProvider 方法里面添加的代碼,是不會在具體業務模塊的初始化模塊呼叫之前被呼叫,但在 Autofac 里面,模塊的初始化順序是模塊加入 Autofac 的順序,因此可以在 FakeAutofacServiceProviderFactory 里面再加入測驗的模塊,測驗的模塊將會是最后加入的模塊,也就是將會最后被執行

因此想要在接入 Autofac 框架覆寫業務邏輯注冊的型別,就需要在 Autofac 里面注冊一個測驗使用的模塊,要求這個模塊最后注冊,然后在此模塊里面進行注冊型別,這樣就可以讓測驗模塊注冊的型別是最后注冊的,覆寫原有的型別,而想要讓測驗模塊最后注冊,就需要自己實作一個繼承 IServiceProviderFactory<ContainerBuilder> 的型別,才能在 AutofacServiceProviderFactory 的 CreateServiceProvider 方法呼叫之前注冊模塊

雖然我很喜歡使用 Autofac 框架,但是我覺得在接入 ASP.NET Core 時,沒有很好加入測驗的機制,而讓開發者需要自己理解底層的邏輯才能進行注冊測驗的型別

這里也需要給 dotnet 的設計點贊,在一開始的 ASP.NET Core 選擇依賴注入框架時,選擇的是 dotnet 通用依賴注入框架,而 dotnet 通用依賴注入框架最底層的是使用最初的裝配器介面,在 C# 語言里面介面的定義是最通用的,介面只約束而不定義,通過這一套傳承的定義,可以讓 10 多年前的 Autofac 框架依然可以跑在現代的應用里面

這 10 多年也不是 Autofac 啥都不做,上面的說法只是為了說明 dotnet 的兼容性特別強和最初的 dotnet 設計大佬的強大

本文的實作方法,雖然代碼很少,但要理解 dotnet 的依賴注入和 ASP.NET Core 的依賴注入使用,和 Autofac 的接入方法,看起來就是 Autofac 的接入機制其實沒有考慮全,當然,也許是我的技術不夠,也許有更好的實作方法,還請大佬們教教我

代碼

本文所有代碼放在 github 和 gitee 歡迎小伙伴訪問

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/285372.html

標籤:.NET Core

上一篇:.NET Core開發實戰 微服務架構最佳實踐

下一篇:ASP.Net Core5.0 EF Core使用記錄

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more