我有一个Bootstrapper,它可以查看ASP.NET MVC应用程序中的所有程序集,找到实现IBootstrapperTask
接口的类型,然后使用IOC Contrainer注册它们。我们的想法是,您可以将您的IBootstrapperTasks放在任何地方,并按照您的喜好组织您的项目。
Bootstrapper代码:
public class Bootstrapper
{
static Bootstrapper()
{
Type bootStrapperType = typeof(IBootstrapperTask);
IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies();
List<Type> tasks = new List<Type>();
foreach (Assembly assembly in assemblies)
{
var types = from t in assembly.GetTypes()
where bootStrapperType.IsAssignableFrom(t)
&& !t.IsInterface && !t.IsAbstract
select t;
tasks.AddRange(types);
}
foreach (Type task in tasks)
{
if (!IocHelper.Container().Kernel.HasComponent(task.FullName))
{
IocHelper.Container().AddComponentLifeStyle(
task.FullName, task, LifestyleType.Transient);
}
}
}
public static void Run()
{
// Get all registered IBootstrapperTasks, call Execute() method
}
}
完整构建后,AppDomain.CurrentDomain.GetAssemblies()
会返回我解决方案中的所有程序集(包括所有GAC,但这并不会让我感到困扰)。
但是,如果重新启动AppDomain,或者我'反弹'Web.Config文件(添加空格并保存),静态构造函数会再次运行,但是当调用AppDomain.CurrentDomain.GetAssemblies()
时,大多数缺少大会,包括我的IBootstrapperTask类型。
如何解决这个问题?我想System.IO可以在/ bin目录下手动加载所有的DLL,但如果可能的话,宁愿避免这种情况,或者这是唯一的方法吗?我采取了正确的一般方法吗?
这是在.NET 4.0上运行的ASP.NET MVC 2.0应用程序,我在内置的Visual Studio 2010 Cassini Web服务器上遇到此问题,在Windows Server 2008上使用集成管道模式的IIS7.0。
编辑:我刚刚看到这篇SO帖Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies,其中说AppDomain只在需要时加载程序集(例如,当程序集中的方法/类是第一个时)所谓的)。我想这可以解释为什么AppDomain.CurrentDomain.GetAssemblies()
上的程序集缺失,因为Bootstrapper很早就运行了。
我注意到,如果我在引导程序之前从缺少的程序集中调用了'某事',例如:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
MyApp.MissingAssembly.SomeClass someClass =
new MyApp.MissingAssembly.SomeClass();
Bootstrapper.Run();
}
}
......似乎解决了这个问题,但这有点像黑客。
答案 0 :(得分:50)
我查看了ASP.NET MVC 2.0源代码并查看了AreaRegistration.RegisterAllAreas();
的实现方式。此行通常放入Global.asax Application_Start()方法,并在内部扫描所有程序集以查找实现AreaRegistration抽象类型的类型。这有点像我追求的行为。
似乎RegisterAllAreas()调用BuildManager.GetReferencedAssemblies()
,如果它对MVC足够好那么它对我来说足够好了: - )
我已经完成了一些实验,BuildManager.GetReferencedAssemblies()甚至会将随机DLL放入/ bin文件夹中,即使没有引用Visual Studio解决方案中的任何项目也是如此。所以它似乎比AppDomain.Current.GetAssemblies()
更可靠。
我已将程序集定位器代码重写为以下内容:
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Compilation;
public static class AssemblyLocator
{
private static readonly ReadOnlyCollection<Assembly> AllAssemblies;
private static readonly ReadOnlyCollection<Assembly> BinAssemblies;
static AssemblyLocator()
{
AllAssemblies = new ReadOnlyCollection<Assembly>(
BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToList());
IList<Assembly> binAssemblies = new List<Assembly>();
string binFolder = HttpRuntime.AppDomainAppPath + "bin\\";
IList<string> dllFiles = Directory.GetFiles(binFolder, "*.dll",
SearchOption.TopDirectoryOnly).ToList();
foreach (string dllFile in dllFiles)
{
AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllFile);
Assembly locatedAssembly = AllAssemblies.FirstOrDefault(a =>
AssemblyName.ReferenceMatchesDefinition(
a.GetName(), assemblyName));
if (locatedAssembly != null)
{
binAssemblies.Add(locatedAssembly);
}
}
BinAssemblies = new ReadOnlyCollection<Assembly>(binAssemblies);
}
public static ReadOnlyCollection<Assembly> GetAssemblies()
{
return AllAssemblies;
}
public static ReadOnlyCollection<Assembly> GetBinFolderAssemblies()
{
return BinAssemblies;
}
}
答案 1 :(得分:1)
看起来你解决了自己的问题。
编辑:就个人而言,我实际上会逐个枚举程序集,加载它们并查找接口。是基于文件而不是AppDomain正在做的事情。