我们使用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));
}
我已将程序集设置为“复制本地”,但无效。我读到了一个Copy Local错误,并尝试了一些不起作用的解决方法。
有没有人能够解决这个问题?似乎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
这应该对你有帮助。它接受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();
这将加载那些甚至没有引用的程序集。
//简答
您是否看过这个:http://docs.autofac.org/en/latest/faq/iis-restart.html看起来您可以扫描引用的程序集并强制它们加载。
//建议(或意见,如果你喜欢)
就个人而言,我不会加载那些只是为了能够为引导创建一个单独的项目而不会被使用的程序集。我没有看到在它自己的项目中拥有它的好处。请记住,DI就是这样,你正在注入“那个”项目的依赖项。每个项目都可以拥有自己的依赖项,关注点的分离来自bootstrapping类,而不是它自己的项目。如果您需要将您的业务逻辑项目分解为公司中其他项目将使用的nuget包,您不希望他们也必须下载DI项目包,而现在又需要其他可能永远不会使用的包。如果您的依赖项包含在需要它们的项目中,它将变为模块化和可扩展的。这部分答案当然值得商榷,但至少应予以考虑。
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();
如果您的解决方案中有多个项目,每个项目都有自己的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()