首页 > 解决方案 > Autofac 模块顺序注册导致对象被注入其默认实例值 (default(T))

问题描述

我面临 Autofac 注册的问题。简而言之,如果我在配置之前注册模型,当我加载配置时,它可以顺利运行,但是,如果我在注册配置之后注册模型,配置模型将加载它们的默认类型(默认(T))。下面是重现问题的代码:

using System;
using System.IO;

using Autofac;

using Microsoft.Extensions.Configuration;

namespace AutofacConfigurationTest.CrossCutting
{
    public class ModuleModel : Module
    {
        protected override void Load(ContainerBuilder containerBuilder)
        {
            containerBuilder.RegisterType<Cache.Configuration>()
                .As<Cache.IConfiguration>();

            containerBuilder.RegisterType<Repository.Configuration>()
                .As<Repository.IConfiguration>();
        }
    }

    public class ModuleConfiguration : Module
    {
        protected override void Load(ContainerBuilder containerBuilder)
        {
            var configurationRoot = new Configuration.Container().ConfigurationRoot;

            containerBuilder.RegisterInstance(configurationRoot).As<IConfigurationRoot>();

            containerBuilder
                .RegisterInstance(configurationRoot.GetSection(Cache.Configuration.Name)
                    .Get<Cache.Configuration>()).As<Cache.IConfiguration>();

            containerBuilder
                .RegisterInstance(configurationRoot.GetSection(Repository.Configuration.Name)
                    .Get<Repository.Configuration>()).As<Repository.IConfiguration>();
        }
    }

    public class Container
    {
        public IContainer Kernel { get; }

        public Container()
        {
            var containerBuilder = new ContainerBuilder();

            // uncomment the line below to make it work //
            containerBuilder.RegisterModule(new ModuleModel()); // if we register the models here, before the configuration, the configuration works properly //

            containerBuilder.RegisterModule(new ModuleConfiguration());

            // comment the line below to make it work //
            containerBuilder.RegisterModule(new ModuleModel()); // if we register the models here, after the configuration, the configuration cannot load the data //

            Kernel = containerBuilder.Build();
        }
    }
}

namespace AutofacConfigurationTest.Configuration
{
    public class Container
    {
        private const string ConfigurationFile = "AppSettings.json";

        public Container()
        {
            ConfigurationRoot = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile(ConfigurationFile).Build();
        }

        public IConfigurationRoot ConfigurationRoot { get; }
    }
}

namespace AutofacConfigurationTest.Cache
{
    public enum Engine
    {
        None,
        Default
    }

    public interface IConfiguration
    {
        Engine Engine { get; set; }
        int Duration { get; set; }
        string ConnectionString { get; set; }
    }

    public class Configuration : IConfiguration
    {
        public const string Name = "Cache";

        public Engine Engine { get; set; }
        public int Duration { get; set; }
        public string ConnectionString { get; set; }
    }
}

namespace AutofacConfigurationTest.Repository
{
    public enum Engine
    {
        None,
        LiteDb
    }

    public interface IConfiguration
    {
        Engine Engine { get; set; }
        string ConnectionString { get; set; }
    }

    public class Configuration : IConfiguration
    {
        public const string Name = "Repository";

        public Engine Engine { get; set; }
        public string ConnectionString { get; set; }
    }
}

namespace AutofacConfigurationTest
{
    internal class Program
    {
        private static IContainer _container;

        private static void RegisterServices() => _container = new CrossCutting.Container().Kernel;

        private static void DisposeServices()
        {
            if (_container != null &&
                _container is IDisposable disposable)
                disposable.Dispose();
        }

        private static void Main(string[] args)
        {
            try
            {
                RegisterServices();

                // the following objects will be have a default(T) instance
                // if the in the Autofac modules the Model is registered AFTER the Configuration

                var cacheConfiguration = _container.Resolve<Cache.IConfiguration>();
                var repositoryConfiguration = _container.Resolve<Repository.IConfiguration>();

                Console.ReadLine();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            finally
            {
                DisposeServices();
            }
        }
    }
}

我有第二个问题要问你:我使用接口来强制我的模型中的合同。简而言之,这些接口从来没有注入任何地方,只是为了让我更容易维护。我应该删除这些模型的 DI/IoC,还是有任何理由将模型注册保留在容器中?

标签: c#.net-coreautofac

解决方案


观察到的行为差异现在取决于模块注册的顺序,这是由于两个模块都在注册与Cache.IConfiguration和键控的服务Repository.IConfiguration。因此,最后注册的模块将“获胜”。

最后注册时ModuleModel,它将覆盖两个配置接口的任何先前注册,并且解析将产生Cache.Configuration和的实例Repository.Configuration

如果ModuleConfiguration最后注册,解析将产生对象提供的实例configurationRoot

从这两个模块推断您的意图,因为ModuleConfiguration注册实际上是在尝试解析Cache.ConfigurationRepository.ConfigurationModuleModel所以必须注册这些类型,这些类型键控到这些类型,而不是键控到接口。您使用.AsSelf()而不是.As<some interface>(). 阅读更多关于AsSelf 这里


推荐阅读