ASP.NET - AppDomain.CurrentDomain.GetAssemblies() - AppDomain 重新启动后程序集丢失
我有一个 Bootstrapper,它会浏览 ASP.NET MVC 应用程序中的所有程序集,以查找实现 IBootstrapperTask 接口的类型,然后将它们注册到 IOC 容器。这个想法是,您实际上可以将 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 时遇到此问题。
编辑: 我刚刚看到这篇文章 AppDomain.GetAssemblies 和 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();
}
}
......它似乎解决了问题,但它有点像黑客。
I have a Bootstrapper that looks through all Assemblies in an ASP.NET MVC application to find types that implement an IBootstrapperTask
interface, and then registers them with an IOC Contrainer. The idea is that you can literaly place your IBootstrapperTasks anywhere, and organise your Projects how you please.
Code for 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
}
}
After a full build, AppDomain.CurrentDomain.GetAssemblies()
returns all Assemblies in my solution (including all the GAC one's but that doesn't bother me).
However, if the AppDomain is restarted, or I 'bounce' the Web.Config file (adding a space and saving), the static constructor is run again but when AppDomain.CurrentDomain.GetAssemblies()
is called, most of the Assemblies are missing, including the one containing my IBootstrapperTask types.
How do I get around this problem? I guess I could System.IO the /bin directory and load all the DLLs in there manually, but would rather avoid this if possible, or is that the only way? Am I taking the right general approach to this?
This is an ASP.NET MVC 2.0 application running on .NET 4.0, I get this problem with the built-in Visual Studio 2010 Cassini web server, and with IIS7.0 in Integrated Pipeline Mode on Windows Server 2008.
Edit: I just came across this SO post Difference between AppDomain.GetAssemblies and BuildManager.GetReferencedAssemblies which says the AppDomain only loads the Assemblies as they're needed (eg. when a method/class from that Assembly is first called). I guess that would explain why the Assemblies are missing on AppDomain.CurrentDomain.GetAssemblies()
as the Bootstrapper is run very early on.
I noticed if I placed a call to 'something' from the missing Assembly before the Bootstrapper eg:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
MyApp.MissingAssembly.SomeClass someClass =
new MyApp.MissingAssembly.SomeClass();
Bootstrapper.Run();
}
}
...it seems to fix the problem, but it is a bit of a hack.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我查看了 ASP.NET MVC 2.0 源代码,并了解了
AreaRegistration.RegisterAllAreas();
是如何实现的。该行通常放入 Global.asax Application_Start() 方法中,并在内部扫描所有程序集以查找实现 AreaRegistration 抽象类型的类型。这就是我所追求的行为。看来 RegisterAllAreas() 调用了
BuildManager.GetReferencedAssemblies()
,如果它对于 MVC 来说足够好,那么它对我来说就足够了:-)我已经做了一些实验和 BuildManager.GetReferencedAssemblies()甚至会拾取放入 /bin 文件夹中的临时、随机 DLL,即使没有引用 Visual Studio 解决方案中的任何项目。因此它看起来比 AppDomain.Current.GetAssemblies() 可靠得多。
我已将程序集定位器代码重写为以下内容:
I looked through the ASP.NET MVC 2.0 source code and looked up how
AreaRegistration.RegisterAllAreas();
is implemented. This line is usually put into the Global.asax Application_Start() method and internally it scans all Assemblies for types that implement the AreaRegistration abstract type. This is kinda the behaviour I'm after.It appears RegisterAllAreas() makes a call to
BuildManager.GetReferencedAssemblies()
, well if it's good enough for MVC then it's good enough for me :-)I've done some experimentation and BuildManager.GetReferencedAssemblies() will even pick up adhoc, random DLL's dropped into the /bin folder, even with no references to any projects in the Visual Studio solution. So it appears far more reliable than
AppDomain.Current.GetAssemblies()
.I've rewritten my Assembly locator code to the following:
看来你解决了你自己的问题。
编辑:就个人而言,我实际上会一一枚举程序集,加载它们并查找接口。根据文件而不是 AppDomain 正在执行的操作来执行此操作。
Looks like you solved your own problem.
Edit: Personally, I would actually enumerate the assemblies, one by one, load them and look for the interface. Do it based of files rather than what the AppDomain is doing.