C# 多项目解决方案中的Autofac

C# 多项目解决方案中的Autofac,c#,dependency-injection,.net-core,autofac,autofac-module,C#,Dependency Injection,.net Core,Autofac,Autofac Module,我使用Autofac作为C#解决方案中的依赖注入系统,该解决方案跨越了几个类库和几个可执行文件。我使用模块来配置Autofac,但这仍然给我留下了构建DI容器的问题,这取决于我为哪个可执行文件编写它 我尝试使用Autofac的RegisterAssemblyModule,但您必须给它一个要扫描的程序集列表,并且在使用类库程序集中的某个类型之前,程序集不会加载,因此无法进行扫描 有些人建议加载bin目录中可能包含Autofac模块定义的每个程序集。但这似乎构成了一个不受欢迎的大会滑向行动的风险 所

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

我尝试使用Autofac的RegisterAssemblyModule,但您必须给它一个要扫描的程序集列表,并且在使用类库程序集中的某个类型之前,程序集不会加载,因此无法进行扫描

有些人建议加载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;
        }
    }
}
公共静态类容器
{
私有静态IContainer_实例;
私有静态字典_moduleAssemblies=新字典();
公共静态无效注册表AutoFacModuleAssembly()
T:在哪里上课
{
var组件=类型(T)。组件;
if(!\u moduleAssemblies.ContainsKey(assembly.FullName))
_moduleAssembly.Add(assembly.FullName,assembly);
}
公共静态IContainer实例
{
得到
{
if(_instance==null)
{
var builder=new ContainerBuilder();
registerasemblymodules(_moduleAssemblies.Select(ma=>ma.Value.ToArray());
_instance=builder.Build();
}
返回_实例;
}
}
}
您可以通过在应用程序的启动代码中包含这样的行来使用它:

public static void Main(string[] args)
{
  AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<ScannerApp>(); 
  AutoFacRegistrar.Container.RegisterAutofacModuleAssembly<AppConfiguration>();
publicstaticvoidmain(字符串[]args)
{
AutoFacRegister.Container.RegisterAutofacModuleAssembly();
AutoFacRegister.Container.RegisterAutofacModuleAssembly();
这是一个合理的解决方案吗?如果有更好、更灵活的解决方案,我有兴趣了解一下

绑定系统中的问题

在执行@Nightowl888的建议时,我遇到了Microsoft configuration
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();
}
protected override void Load(ContainerBuilder生成器)
{
基础荷载(建筑商);
var config=new ConfigurationBuilder()
.AddJsonFile(AppConfiguration.WebJobsConfigFile,false)
.AddUserSecrets()
.AddenEnvironmentVariables()
.Build();
建造商注册((c,p)=>
{
var retVal=新的AppConfiguration(c.Resolve());
config.Bind(retVal);
返回返回;
} )
.SingleInstance();
}
问题发生在对Bind()的调用中。当它遍历和解析配置信息时,它希望通过无参数构造函数创建各种对象……这使得使用构造函数injection变得困难

如果我不能使用构造函数注入,我需要能够在构造函数代码中针对DI容器进行解析。我不知道如何定义一个不会硬连接特定DI容器解析语义的库程序集


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

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

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

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

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


Thanx,这很有帮助。但在尝试实现该方法时,我遇到了一个问题,我在原始问题的附录中描述了这个问题。我对您关于解决这个问题的想法很感兴趣。抱歉,我还没有机会使用
IOptions
功能。我建议问一个新问题,因为这个问题的答案是我Thanx对这个建议很感兴趣,但我可能会把它传下去。因为我们“说”了,我已经重写了配置例程,这样它就可以利用Microsoft的配置系统,但不需要DI,除了“最上面的”配置类(我最初的问题是,我试图通过配置类向下传递Serilog ILogger实例,以便在对它们执行验证时,它们会记录它们遇到的任何问题)。