多项目解决方案中的 Autofac

Autofac in a Multi-Project Solution

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

我尝试使用 Autofac 的 RegisterAssemblyModules,但你必须给它一个要扫描的程序集列表,直到使用 class 库程序集中的某些类型,程序集才不会加载,因此不会可供扫描。

有些人建议加载 bin 目录中的每个程序集,其中可能包含 Autofac 模块定义。但这似乎会带来不受欢迎的程序集开始运行的风险。

所以我想到的是这个静态 class,它在一个公共 class 库中定义:

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 容器的解析语义中硬连线。

想法?除了我考虑过的 "abandon the IOptions<> system" 之外,它还提供了一些我想保留的好处。

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

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

如果您要使用 autofac 模块,它们应该是使用它们的应用程序的一部分,而不是包含在正在组合的组件的程序集中。虽然不必在每个应用程序中重复配置代码,看起来您似乎有所收获,但这样做的主要问题是,这意味着您的应用程序失去了 DI 的主要优势之一——即,它无法提供替代 任何给定组件的实现。使库松散耦合的全部意义在于,它允许决定应用程序如何耦合在一起,最终由托管组件的应用程序决定。

Pet peeve: Also note that how many projects or solutions you have has absolutely nothing to do with the runtime behavior of an application (such as how it is composed). Projects and solutions are a way to organize code before it is compiled - once the code is compiled, there is no concept of "project" or "solution", all you are left with are "assemblies" that may depend on other "assemblies". For each application you end up with an executable assembly and 0 or more dependent assemblies. The composition root should only exist in the executable assembly.