GetEntryAssembly for web applications

耗尽温柔 提交于 2019-11-27 04:27:59

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

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

As an answer to my own question (some people here are really touchy on accepted rate) => I did not find a better way than the code given in the question.

That means te solution is not perfect but it work s long as your base Page is defined in the front-end assembly.

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.

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;
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!