用于 Web 应用程序的 GetEntryAssembly

发布于 2024-10-04 01:07:31 字数 2616 浏览 5 评论 0原文

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

但是...我真的需要这样的东西。 我使用一些在 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文件

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

我现在要寻找的是:

1)我的装配验证条件是否足够? (我可能忘记了案例)

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

Assembly.GetEntryAssembly() does not work for web applications.

But... I really need something like that.
I work with some deeply-nested code that is used in both web and non-web applications.

My current solution is to browse the StackTrace to find the first called assembly.

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

To ensure the assembly is what we want, we have 3 conditions :

  • the assembly is not in the GAC
  • the assembly is no dynamic
  • the assembly is not generated (to avoid temporary asp.net files

The last problem I meet is when the base page is defined in a separate assembly.
(I use ASP.Net MVC, but it'll be the same with ASP.Net).
In that particular case, it's that separate assembly that is returned, not the one containing the page.

What I am looking for now is :

1) Are my assembly validation conditions enough ? (I may have forgotten cases)

2) Is there a way, from a given code-generated assembly in the ASP.Net temporary folder, to get information about the project that contains that Page / View ?
(I think not, but who knows...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夏末 2024-10-11 01:07:31

这似乎是获取 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;
    }

This seems to be a reliable, simple way to get the "entry" or main assembly for a web app.

If you put controllers in a separate project, you may find that the base class of ApplicationInstance is not in the same assembly as your MVC project that contains the Views - but, this setup seems pretty rare (I mention it because I've tried this setup at one point, and a while back a few blogs supported the idea).

    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;
    }
三生一梦 2024-10-11 01:07:31

就我而言,我需要在初始化 System.Web.HttpContext.Current.ApplicationInstance 之前获取 Web 应用程序的“入口程序集”。另外,我的代码需要适用于各种应用程序类型(窗口服务、桌面应用程序等),并且我不喜欢因 Web 问题而污染我的常用代码。

我创建了一个自定义程序集级属性,可以在要指定为入口点程序集的程序集的 AssembyInfo.cs 文件中声明该属性。然后,您只需调用该属性的静态 GetEntryAssembly 方法即可获取条目程序集。如果 Assembly.GetEntryAssembly 返回非 null,则使用该值,否则它将在加载的程序集中搜索具有自定义属性的程序集。结果被缓存在Lazy中。

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();
        }
    }
}

In my case, I needed to get the "entry assembly" for a web app before System.Web.HttpContext.Current.ApplicationInstance is initialized. Also, my code needed to work for a variety of app types (window services, desktop apps, etc), and I don't like to pollute my common code with Web concerns.

I created a custom assembly-level attribute, which can be declared in the AssembyInfo.cs file of an assembly which you want to designate as the entry point assembly. Then, you just call the attribute's static GetEntryAssembly method to get the entry assembly. If Assembly.GetEntryAssembly returns non-null, that is used, otherwise it searches through loaded assemblies for the one with the custom attribute. The result is cached in a 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();
        }
    }
}
轻拂→两袖风尘 2024-10-11 01:07:31

问题中提出的算法确实对我有用,而使用 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 是包含当前请求处理程序入口点的代码模块。 Handler.GetType().Assembly 是一个临时的 ASP.Net 程序集,但 Handler.GetType().BaseType.Assembly 是我的应用程序的真正“入口程序集”。我很好奇这是否也适用于其他各种 ASP.Net 应用程序类型。

The algorithm proposed in the question did indeed work for me, whereas the method using System.Web.HttpContext.Current.ApplicationInstance didn't. I think my problem is that the old-style ASP.Net application for which I need a solution lacks a global.asax handler.

This shorter solution also worked for me and I think will generally work on the condition that the page handler is defined in the front-end assembly:

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

My application is an ASP.Net 4.x web forms application. For this application type, HttpContext.Current.Handler is the code module containing the entry point of the current request handler. Handler.GetType().Assembly is a temporary ASP.Net assembly, but Handler.GetType().BaseType.Assembly is the true "entry assembly" of my application. I am curious if the same works for various other ASP.Net application types.

油饼 2024-10-11 01:07:31

我能够使其在 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;

The only way I was able to get this to work consistently for Web Applications (at least in .NET 4.5.1) was to do the Assembly.GetExecutingAssembly() in the Web Application project itself.

If you try to create a utilities project with static methods and do the call there, you will get the assembly information from that assembly instead - for both GetExecutingAssembly() and GetCallingAssembly().

GetExecutingAssembly() is a static method that returns an instance of the Assembly type. The method does not exist on an instance of the Assembly class itself.

So, what I did was created a class that accepts Assembly type in the constructor, and created an instance of this class passing the results from 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;
        }
    }
}

And to use it

string Description = new WebAssemblyInfo(Assembly.GetExecutingAssembly()).Description;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文