C# 用于web应用程序的GetEntryAssembly
Assembly.GetEntryAssembly()不适用于web应用程序 但是。。。我真的需要这样的东西。 我使用一些在web和非web应用程序中都使用的深度嵌套代码 我当前的解决方案是浏览StackTrace以查找第一个调用的程序集C# 用于web应用程序的GetEntryAssembly,c#,reflection,assemblies,code-generation,stack-frame,C#,Reflection,Assemblies,Code Generation,Stack Frame,Assembly.GetEntryAssembly()不适用于web应用程序 但是。。。我真的需要这样的东西。 我使用一些在web和非web应用程序中都使用的深度嵌套代码 我当前的解决方案是浏览StackTrace以查找第一个调用的程序集 /// <summary> /// Version of 'GetEntryAssembly' that works with web applications /// </summary> /// <returns>The
/// <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;
}
//
///用于web应用程序的“GetEntryAssembly”版本
///
///条目程序集,或web应用程序中第一个调用的程序集
公共静态程序集GetEntyAssembly()
{
//获取入口程序集
var result=Assembly.GetEntryAssembly();
//如果没有(例如:web应用程序)
如果(结果==null)
{
//现行方法
MethodBase methodCurrent=null;
//要跳过的帧数
int framestoSkip=1;
//循环,直到我们无法在stacktrace中找到更多
做
{
//获取堆栈帧,跳过给定的帧数
StackFrame StackFrame=新的StackFrame(framestoSkip);
//获取方法
methodCurrent=stackFrame.GetMethod();
//如果找到
如果((methodCurrent!=null)
//如果该方法未从堆栈跟踪中排除
&&(methodCurrent.GetAttribute(false)==null))
{
//得到它的类型
var类型电流=methodCurrent.DeclaringType;
//如果有效
if(typeCurrent!=typeof(RuntimeMethodHandle))
{
//把它组装起来
var assembly=typeCurrent.assembly;
//如果有效
如果(!assembly.GlobalAssemblyCache
&&!assembly.IsDynamic
&&(assembly.GetAttribute()==null))
{
//然后我们找到了一个有效的程序集,将其作为候选程序集
结果=组装;
}
}
}
//增加要跳过的帧数
framestoSkip++;
}//虽然我们有一个工作方法
while(methodCurrent!=null);
}
返回结果;
}
为确保组装符合我们的要求,我们有3个条件:
- 该程序集不在GAC中
- 组件不是动态的
- 未生成程序集(以避免临时asp.net文件
(我认为不是,但谁知道…这似乎是一种可靠、简单的方法,可以获取web应用程序的“条目”或主程序集 如果将控制器放在一个单独的项目中,您可能会发现ApplicationInstance的基类与包含视图的MVC项目不在同一个程序集中-但是,这种设置似乎非常罕见(我提到它是因为我曾经尝试过这种设置,不久前一些博客支持这种想法)
问题中提出的算法确实适用于我,而使用System.Web.HttpContext.Current.ApplicationInstance的方法则不适用。我认为我的问题在于我需要解决方案的旧式ASP.Net应用程序缺少global.asax处理程序 这个较短的解决方案也适用于我,我认为通常在前端程序集中定义页面处理程序的情况下也适用:
private静态程序集GetMyEntryAssembly()
{
if((System.Web.HttpContext.Current==null)| |(System.Web.HttpContext.Current.Handler==null))
返回Assembly.GetEntryAssembly();//不是web应用程序
返回System.Web.HttpContext.Current.Handler.GetType().BaseType.Assembly;
}
我的应用程序是一个ASP.NET4.xWebForms应用程序。对于此应用程序类型,HttpContext.Current.Handler是包含当前请求处理程序入口点的代码模块。Handler.GetType().Assembly是一个临时ASP.Net程序集,但Handler.GetType().BaseType.Assembly是我的应用程序的真正“条目程序集”。我很好奇这是否适用于其他各种ASP.Net应用程序类型。我能够使这一点在Web应用程序(至少在.Net 4.5.1中)上保持一致的唯一方法是在Web应用程序项目本身中执行Assembly.GetExecutionGassembly() 如果您尝试使用静态方法创建一个实用程序项目并在那里进行调用,那么您将从该程序集中获取程序集信息—对于getExecutionGassembly()和GetCallingAssembly() GetExecutionGassembly()是一个静态方法,返回程序集类型的实例。该方法不存在于程序集类本身的实例上 因此,我所做的是创建一个类,该类在构造函数中接受程序集类型,并创建了一个此类的实例,该实例传递Assembly.getExecutionGassembly()的结果
在我的例子中,我需要在初始化System.web.HttpContext.Current.ApplicationInstance之前获取web应用程序的“条目程序集”。此外,我的代码需要适用于各种应用程序类型(窗口服务、桌面应用程序等),我不喜欢因为网络问题而污染我的通用代码 我创建了一个自定义程序集级别属性,该属性可以在要指定为入口点程序集的程序集的AssembyInfo.cs文件中声明。然后,您只需调用属性的静态GetEntryAssembly方法来获取条目程序集。如果Assembly.GetEntryAssemb
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;
}
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;
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();
}
}
}