2010-11-25 49 views
36

Assembly.GetEntryAssembly()不适用于Web应用程序。用于Web应用程序的GetEntryAssembly

但是...我真的需要这样的东西。 我使用一些在web和非web应用程序中使用的深嵌套代码。

我目前的解决方案是浏览StackTrace以查找第一个调用程序集。

/// <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; 
} 

确保大会是我们想要的,我们有3个条件:

  • 大会是不是在GAC
  • 大会没有动态
  • 不产生装配(以避免临时asp.net文件

我遇到的最后一个问题是当基本页面被定义为单独的sembly。 (我使用ASP.Net MVC,但它将与ASP.Net一样)。 在这种特殊情况下,返回的是单独的程序集,而不是包含页面的程序集。

我正在寻找的是:

1)我的程序集验证条件是否足够? (我可能已经忘记了案例)

2)有没有一种方法,从ASP.Net临时文件夹中给定的代码生成的程序集中获取有关包含Page/View的项目的信息? (我想不会,但谁知道...)

+1

“入口程序集”在ASP.NET应用程序中并没有任何意义,因为许多ASP.NET应用程序都是在适当的时间串联执行代码的大量程序集。你究竟在做什么? – Levi 2010-11-27 10:42:05

+0

我完全同意你,一个Web应用程序作为“入门程序集”。实际上,我们可以认为他们有多个切入点。我最终需要的是在入口程序集中获取AssemblyInfo.cs文件中的内容。为什么我想要这样做,这不是重点。 – Mose 2010-12-01 13:00:21

+1

我需要做一个类似的任务,并且通常找不到比这更好的方法。 ExcludeFromStackTraceAttribute是你的一类吗?我似乎无法在BCL中找到它。 `GetAttribute <>`同样的问题是,你为了方便而创建了一个方法吗? – 2011-07-19 19:38:48

回答

43

这似乎是一个可靠,简单的方法来获取Web应用程序的“入口”或主要程序集。

如果您将控制器放在单独的项目中,您可能会发现ApplicationInstance的基类与包含视图的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; 
    } 
4

的回答为我自己的问题(这里有些人是公认的速度真的敏感) =>我没有找到比中给出的代码更好的方法这个问题。

这意味着te解决方案并不完美,但只要您的基本页面是在前端装配体中定义的,就可以工作。

2

在这个问题对我来说确实工作提出的算法,而使用System.Web.HttpContext.Current.ApplicationInstance方法没有。我认为我的问题是,我需要解决方案的旧式ASP.Net应用程序缺少global.asax处理程序。

这短的解决方案也为我工作,我想一般会在该页面的处理程序是在前端集中定义的条件下工作:

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表单应用程序。对于此应用程序类型,HttpContext.Current。处理程序是包含当前请求处理程序入口点的代码模块。 Handler.GetType()。Assembly是一个临时的ASP.Net程序集,但Handler.GetType()。BaseType.Assembly是我的应用程序真正的“入口程序集”。我很好奇,如果同样适用于各种其他ASP.Net应用程序类型。

-1

我能够一致地为Web应用程序工作(至少在.NET 4.5.1中)的唯一方法是在Web应用程序项目本身中执行Assembly.GetExecutingAssembly()。

如果您尝试使用静态方法创建实用程序项目并在那里执行调用,您将从该程序集中获取程序集信息 - 对于GetExecutingAssembly()和GetCallingAssembly()。

GetExecutingAssembly()是一个返回Assembly类型实例的静态方法。该方法不存在于Assembly类本身的一个实例中。

因此,我所做的创建了一个接受构造函数中的Assembly类型的类,并创建了此类的一个实例,从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; 
5

就我而言,我需要得到“条目集结号”的web应用程序System.Web.HttpContext.Current.ApplicationInstance初始化之前。此外,我的代码需要适用于各种应用程序类型(窗口服务,桌面应用程序等),我不喜欢用Web关注来污染我的通用代码。

我创建了一个自定义的程序集级属性,该属性可以在要指定为入口点程序集的程序集的AssembyInfo.cs文件中声明。然后,您只需调用该属性的静态GetEntryAssembly方法即可获取条目组件。如果Assembly.GetEntryAssembly返回非空值,则使用该值,否则它将搜索加载的程序集中的自定义属性。结果缓存在一个懒惰的<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(); 
     } 
    } 
}