【问题标题】:Autofac: How to load assemblies that are referenced but not directly usedAutofac:如何加载被引用但未直接使用的程序集
【发布时间】:2016-02-22 00:23:04
【问题描述】:

我们使用 Autofac for DI 创建了一个 WebApi 解决方案。我们将 autofac 的引导分解为一个单独的项目。这样,我们的 WebApi 项目只引用了我们的 Bootstrap 和 Contracts 项目。然后,我们的引导项目引用所有其他程序集并将所有内容连接在一起。我喜欢这种关注点分离的设计。

我们可以按如下方式手动加载我们的程序集 - 我们的“AutofacModule”类包含注册每个模块(程序集)所需的信息。

ContainerBuilder builder = new Autofac.ContainerBuilder();
builder.RegisterModule(new Business.AutofacModule());
builder.RegisterModule(new Data.AutofacModule());
builder.RegisterModule(new Services.AutofacModule());
etc...

这可行,但需要对每个程序集进行硬编码。我们正在尝试使这种动态化,以便我们可以循环遍历所有引用的程序集,如下所示。

var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>();
foreach (var assembly in assemblies)
{
    builder.RegisterAssemblyModules(assembly);
}

这应该有效,但无效。问题是.Net 确定引导项目中实际上并未使用各种程序集并且不加载它们(试图优化?)。所以我们的一些程序集永远不会加载。

我还尝试了以下方法来遍历 bin 目录以查找所有程序集。但是,在编译期间,.Net 不会将未引用的程序集移动到 bin 目录中,因此它们也不存在。

string assemblyPath = System.IO.Path.Combine(
    System.AppDomain.CurrentDomain.BaseDirectory, "bin");
var allAssemblies = new List<Assembly>();
foreach (string dll in Directory.GetFiles(assemblyPath, "*.dll"))
{
    allAssemblies.Add(Assembly.LoadFile(dll));
}

我已将程序集设置为“复制本地”,但这不起作用。我阅读了有关复制本地错误的信息,并尝试了解决方法,但这些错误也不起作用。

有人能解决这个问题吗?看起来 Autofac 会提供一个解决方案,但我发现的只是他们文档上的一个待办事项页面: http://autofac.readthedocs.org/en/latest/faq/isolate-autofac.html

以下两个问题相似,但没有一个建议的解决方案能够克服所需程序集不在 bin 目录中的事实。

Not all assemblies are being loaded into AppDomain from the bin folder

Loading all referenced assemblies .NET even if not used explicitly in code

最后,我很好奇,这是 Autofac 特有的问题吗?其他 DI 容器是如何解决这个问题的?我为 NInject 发现了一个类似的问题。 Loading unreferenced dll MVC Ninject

【问题讨论】:

  • 我正在使用 Autofac 在运行时使用“.RegisterAssemblyModules”方法从程序集中动态加载模块,它就像一个魅力。唯一的要求是任何应该加载的程序集都必须以特定的后缀(对于 Directory.GetFiles)结尾,例如“*.Plugin.dll”。
  • 谢谢@Diets。我们的问题是引导项目引用但不直接使用我们的许多程序集。所以我认为编译器认为它们没有必要,所以它永远不会将它们移动到 bin 目录中。所以我没有什么要加载的。
  • 我的所有“*.Plugin.dll”文件都通过一个为发布收集程序集的构建后操作移动到 \bin 文件夹。

标签: c# .net asp.net-web-api dependency-injection autofac


【解决方案1】:

如果您的解决方案中有多个项目,每个项目都有自己的 IoC 容器类,请使用以下内容加载所有程序集,然后在该程序集中注册每个模块。

这是ContainerBuilder的扩展方法。

public static class ContainerBuilderExtensions
{
    public static void ScanAssembly(this ContainerBuilder builder, string searchPattern = "SolutionName.*.dll")
    {
        var path = AppDomain.CurrentDomain.BaseDirectory;
        foreach (var assembly in Directory.GetFiles(path, searchPattern).Select(Assembly.LoadFrom))
        {
            builder.RegisterAssemblyModules(assembly);
        }
    }
}

上面可以在你的 ContainerBuilder 实例上调用如下:

builder.ScanAssembly()

【讨论】:

    【解决方案2】:
    var assemblies = Directory.EnumerateFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll", SearchOption.TopDirectoryOnly)
       .Where(filePath => Path.GetFileName(filePath).StartsWith("your name space"))
       .Select(Assembly.LoadFrom);
    
    var builder = new ContainerBuilder();
    
    builder.RegisterAssemblyTypes(assemblies.ToArray())
       .Where(t => typeof(ITransientDependency).IsAssignableFrom(t))
       .AsImplementedInterfaces();
    

    【讨论】:

      【解决方案3】:

      这应该对您有所帮助。它获取 bin 文件夹中的所有程序集,以名称 MyModule 开头。

         //builder.RegisterType(typeof(IFoo)).AsImplementedInterfaces();
              ContainerBuilder builder = new ContainerBuilder();
      
              string[] assemblyScanerPattern = new[] { @"MyModule.*.dll"};
      
              // Make sure process paths are sane...
              Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
      
              //  begin setup of autofac >>
      
              // 1. Scan for assemblies containing autofac modules in the bin folder
              List<Assembly> assemblies = new List<Assembly>();
              assemblies.AddRange(
                  Directory.EnumerateFiles(Directory.GetCurrentDirectory(), "*.dll", SearchOption.AllDirectories)
                           .Where(filename => assemblyScanerPattern.Any(pattern => Regex.IsMatch(filename, pattern)))
                           .Select(Assembly.LoadFrom)
                  );
      
      
              foreach (var assembly in assemblies)
              {
                  builder.RegisterAssemblyTypes(assembly )
                      .AsImplementedInterfaces();
              }
              var container = builder.Build();
      

      这将加载那些甚至没有被引用的程序集。

      【讨论】:

      • builder.RegisterAssemblyTypes 接受程序集数组,每个都不是必需的
      【解决方案4】:

      //简答

      您看到了吗:http://docs.autofac.org/en/latest/faq/iis-restart.html 看起来您可以扫描引用的程序集并强制它们加载。

      //建议(或意见,如果你喜欢)

      就我个人而言,我不会加载那些不会被使用的程序集,只是为了能够有一个单独的项目进行引导。我没有看到将它放在自己的项目中的好处。请记住,DI 就是这样,您正在注入“那个”项目的依赖项。每个项目都可以有自己的依赖关系,关注点的分离来自引导类,而不是它在自己的项目中。如果您只需要将业务逻辑项目分解为公司中其他项目将使用的 nuget 包,您不希望他们也必须下载 DI 项目包,而后者现在需要其他可能永远不会使用的包。如果您的依赖项包含在需要它们的项目中,它就会成为模块化和可扩展的。这部分答案当然是有争议的,但至少应该考虑一下。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-02
        • 1970-01-01
        • 1970-01-01
        • 2019-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多