GetEntryAssembly为Web应用程序
Assembly.GetEntryAssembly()不适用于Web应用程序。
但是…我真的需要这样的东西。 我使用一些在web和非web应用程序中使用的深度嵌套代码。
我目前的解决scheme是浏览StackTracefind第一个被调用的程序集。
/// <summary> /// Version of 'GetEntryAssembly' that works with web applications /// </summary> /// <returns>The entry assembly, or the first called assembly in a web application</returns> public static Assembly GetEntyAssembly() { // get the entry assembly var result = Assembly.GetEntryAssembly(); // if none (ex: web application) if (result == null) { // current method MethodBase methodCurrent = null; // number of frames to skip int framestoSkip = 1; // loop until we cannot got further in the stacktrace do { // get the stack frame, skipping the given number of frames StackFrame stackFrame = new StackFrame(framestoSkip); // get the method methodCurrent = stackFrame.GetMethod(); // if found if ((methodCurrent != null) // and if that method is not excluded from the stack trace && (methodCurrent.GetAttribute<ExcludeFromStackTraceAttribute>(false) == null)) { // get its type var typeCurrent = methodCurrent.DeclaringType; // if valid if (typeCurrent != typeof (RuntimeMethodHandle)) { // get its assembly var assembly = typeCurrent.Assembly; // if valid if (!assembly.GlobalAssemblyCache && !assembly.IsDynamic && (assembly.GetAttribute<System.CodeDom.Compiler.GeneratedCodeAttribute>() == null)) { // then we found a valid assembly, get it as a candidate result = assembly; } } } // increase number of frames to skip framestoSkip++; } // while we have a working method while (methodCurrent != null); } return result; }
为了确保组装是我们想要的,我们有三个条件:
- 该组件不在GAC中
- 大会是没有dynamic的
- 程序集不生成(避免临时的asp.net文件
我遇到的最后一个问题是基本页面是在单独的程序集中定义的。 (我使用ASP.Net MVC,但它将与ASP.Net相同)。 在这种情况下,返回的是单独的程序集,而不是包含页面的程序集。
我现在正在寻找的是:
1)我的程序集validation条件是否足够? (我可能已经忘记了案件)
2)有没有一种方法,从ASP.Net临时文件夹中给定的代码生成的程序集,获取有关包含该页面/视图的项目的信息? (我想不是,但谁知道…)
这似乎是一个可靠的,简单的方法来获取Web应用程序的“入口”或主要程序集。
如果将控制器放在单独的项目中,您可能会发现ApplicationInstance的基类与包含Views的MVC项目不在同一个程序集中 – 但是,这个设置看起来很罕见(我提到它是因为我已经试过了设置在一个点,一段时间后面的博客支持这个想法)。
static private Assembly GetWebEntryAssembly() { if (System.Web.HttpContext.Current == null || System.Web.HttpContext.Current.ApplicationInstance == null) { return null; } var type = System.Web.HttpContext.Current.ApplicationInstance.GetType(); while (type != null && type.Namespace == "ASP") { type = type.BaseType; } return type == null ? null : type.Assembly; }
作为我自己的问题的回答(有些人在这里接受的速度真的很敏感)=>我没有find比问题中给出的代码更好的方法。
这意味着te的解决scheme并不完美,但只要在前端程序集中定义基本页面,它就可以工作。
在我的情况下,我需要在System.Web.HttpContext.Current.ApplicationInstance初始化之前获取Web应用程序的“入口程序集”。 另外,我的代码需要适用于各种应用程序types(窗口服务,桌面应用程序等),我不喜欢用Web关注来污染我的通用代码。
我创build了一个自定义的程序集级属性,可以在要指定为入口点程序集的程序集的AssembyInfo.cs文件中声明该属性。 然后,您只需调用该属性的静态GetEntryAssembly方法来获取条目程序集。 如果Assembly.GetEntryAssembly返回非空值,则使用该值,否则,它将search加载的程序集中的自定义属性。 结果caching在Lazy <T>中。
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; namespace EntryAssemblyAttributeDemo { /// <summary> /// For certain types of apps, such as web apps, <see cref="Assembly.GetEntryAssembly"/> /// returns null. With the <see cref="EntryAssemblyAttribute"/>, we can designate /// an assembly as the entry assembly by creating an instance of this attribute, /// typically in the AssemblyInfo.cs file. /// <example> /// [assembly: EntryAssembly] /// </example> /// </summary> [AttributeUsage(AttributeTargets.Assembly)] public sealed class EntryAssemblyAttribute : Attribute { /// <summary> /// Lazily find the entry assembly. /// </summary> private static readonly Lazy<Assembly> EntryAssemblyLazy = new Lazy<Assembly>(GetEntryAssemblyLazily); /// <summary> /// Gets the entry assembly. /// </summary> /// <returns>The entry assembly.</returns> public static Assembly GetEntryAssembly() { return EntryAssemblyLazy.Value; } /// <summary> /// Invoked lazily to find the entry assembly. We want to cache this value as it may /// be expensive to find. /// </summary> /// <returns>The entry assembly.</returns> private static Assembly GetEntryAssemblyLazily() { return Assembly.GetEntryAssembly() ?? FindEntryAssemblyInCurrentAppDomain(); } /// <summary> /// Finds the entry assembly in the current app domain. /// </summary> /// <returns>The entry assembly.</returns> private static Assembly FindEntryAssemblyInCurrentAppDomain() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var entryAssemblies = new List<Assembly>(); foreach (var assembly in assemblies) { // Note the usage of LINQ SingleOrDefault. The EntryAssemblyAttribute's AttrinuteUsage // only allows it to occur once per assembly; declaring it more than once results in // a compiler error. var attribute = assembly.GetCustomAttributes().OfType<EntryAssemblyAttribute>().SingleOrDefault(); if (attribute != null) { entryAssemblies.Add(assembly); } } // Note that we use LINQ Single to ensure we found one and only one assembly with the // EntryAssemblyAttribute. The EntryAssemblyAttribute should only be put on one assembly // per application. return entryAssemblies.Single(); } } }
问题中提出的algorithm确实对我有效,而使用System.Web.HttpContext.Current.ApplicationInstance的方法没有。 我认为我的问题是,我需要一个解决scheme的旧式ASP.Net应用程序缺less一个global.asax处理程序。
这个较短的解决scheme也适用于我,我认为一般工作的条件是页面处理程序是在前端程序集中定义的:
private static Assembly GetMyEntryAssembly() { if ((System.Web.HttpContext.Current == null) || (System.Web.HttpContext.Current.Handler == null)) return Assembly.GetEntryAssembly(); // Not a web application return System.Web.HttpContext.Current.Handler.GetType().BaseType.Assembly; }
我的应用程序是一个ASP.Net 4.x Web窗体应用程序。 对于此应用程序types,HttpContext.Current.Handler是包含当前请求处理程序入口点的代码模块。 Handler.GetType()。Assembly是一个临时的ASP.Net程序集,但Handler.GetType()。BaseType.Assembly是我的应用程序真正的“入口程序集”。 我很好奇,如果相同的作品为其他各种ASP.Net应用程序types。
我能够一致地为Web应用程序工作(至less在.NET 4.5.1中)的唯一方法是在Web应用程序项目中执行Assembly.GetExecutingAssembly()。
如果您尝试使用静态方法创build一个实用程序项目并在那里执行调用,那么您将从该程序集中获取程序集信息 – 对于GetExecutingAssembly()和GetCallingAssembly()。
GetExecutingAssembly()是一个返回Assemblytypes的实例的静态方法。 该方法在Assembly类本身的一个实例上不存在。
所以,我所做的是创build了一个接受构造函数中的Assemblytypes的类,并创build了一个从Assembly.GetExecutingAssembly()传递结果的类的实例。
public class WebAssemblyInfo { Assembly assy; public WebAssemblyInfo(Assembly assy) { this.assy = assy; } public string Description { get { return GetWebAssemblyAttribute<AssemblyDescriptionAttribute>(a => a.Description); } } // I'm using someone else's idea below, but I can't remember who it was private string GetWebAssemblyAttribute<T>(Func<T, string> value) where T : Attribute { T attribute = null; attribute = (T)Attribute.GetCustomAttribute(this.assy, typeof(T)); if (attribute != null) return value.Invoke(attribute); else return string.Empty; } } }
并使用它
string Description = new WebAssemblyInfo(Assembly.GetExecutingAssembly()).Description;