2

我在跨多个类库和多个可执行文件的 C# 解决方案中使用 Autofac 作为依赖注入系统。我正在使用模块来配置 Autofac,但这仍然给我留下了构建 DI 容器的问题,这取决于我为哪个可执行文件编写它。

我尝试使用 Autofac 的 RegisterAssemblyModules,但您必须为其提供要扫描的程序集列表,并且在使用类库程序集中的某些类型之前,不会加载程序集,因此无法扫描。

有些人建议加载 bin 目录中可能包含 Autofac 模块定义的每个程序集。但这似乎带来了一个不受欢迎的大会陷入行动的风险。

所以我想出的是这个静态类,它定义在一个通用的类库中:

public static class Container
{
    private static IContainer _instance;
    private static Dictionary<string, Assembly> _moduleAssemblies = new Dictionary<string, Assembly>();

    public static void RegisterAutofacModuleAssembly<T>()
        where T : class
    {
        var assembly = typeof(T).Assembly;

        if( !_moduleAssemblies.ContainsKey( assembly.FullName ) )
            _moduleAssemblies.Add( assembly.FullName, assembly );
    }

    public static IContainer Instance
    {
        get
        {
            if( _instance == null )
            {
                var builder = new ContainerBuilder();

                builder.RegisterAssemblyModules( _moduleAssemblies.Select( ma => ma.Value ).ToArray() );

                _instance = builder.Build();
            }

            return _instance;
        }
    }
}

您可以通过在应用程序的启动代码中包含这样的行来使用它:

public static void Main(string[] args)
{
  AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<ScannerApp>(); 
  AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<AppConfiguration>();

这是一个合理的解决方案吗?如果有更好,更灵活的,我会有兴趣了解它。

IOptions<>绑定系统问题

在执行@Nightowl888 的建议时,我遇到了Microsoft 配置IOptions<>系统的问题。这是我尝试配置 Autofac 以解析 AppConfiguration 对象的方式:

protected override void Load( ContainerBuilder builder )
{
    base.Load( builder );

    var config = new ConfigurationBuilder()
        .AddJsonFile( AppConfiguration.WebJobsConfigFile, false )
        .AddUserSecrets<ConfigurationModule>()
        .AddEnvironmentVariables()
        .Build();

    builder.Register<AppConfiguration>( ( c, p ) =>
        {
            var retVal = new AppConfiguration( c.Resolve<ILogger>() );

            config.Bind( retVal );

            return retVal;

        } )
        .SingleInstance();
}

问题出现在对 Bind() 的调用中。当它遍历和解析配置信息时,它期望通过无参数构造函数创建各种对象......这使得使用构造函数注入变得困难。

如果我不能使用构造函数注入,我需要能够在构造函数代码中解析 DI 容器。我看不到如何定义一个不在特定 DI 容器的解析语义中硬连线的库程序集。

想法?除了我考虑过的“放弃IOptions<>系统”之外,它还提供了许多我想保持的好处。

4

1 回答 1

2

每个可执行应用程序都应该有自己独特的 DI 配置。框架应该构建为对 DI 友好,但实际上不引用任何 DI 容器。

组合根是应用程序的配置。在应用程序之间共享它类似于在应用程序.config之间共享文件 - 也就是说,通常不会这样做。请参阅组合根重用

如果您要使用 autofac 模块,它们应该是使用它们的应用程序的一部分,而不是与正在组合的组件一起包含在程序集中。虽然您似乎通过不必在每个应用程序中重复配置代码而获得了一些东西,但这样做的主要问题是这意味着您的应用程序已经失去了 DI 的主要好处之一——也就是说,它不能提供任何给定组件的替代实现。使库松散耦合的全部意义在于,它允许最终由托管组件的应用程序决定应用程序如何耦合在一起。

Pet peeve:还要注意,您有多少项目或解决方案与应用程序的运行时行为(例如它的组成方式)完全无关。项目和解决方案是在编译代码之前组织代码的一种方式——一旦代码被编译,就没有“项目”或“解决方案”的概念,剩下的只是可能依赖于其他“程序集”的“程序集” . 对于每个应用程序,您最终都会得到一个可执行程序集和 0 个或多个依赖程序集。组合根应该只存在于可执行程序集中。

于 2018-01-03T07:25:15.223 回答