GetEntryAssembly для веб-приложений - PullRequest
41 голосов
/ 25 ноября 2010

Assembly.GetEntryAssembly () не работает для веб-приложений.

Но ... Мне действительно нужно что-то подобное.Я работаю с некоторым глубоко вложенным кодом, который используется как в веб-приложениях, так и в не-веб-приложениях.

Мое текущее решение состоит в том, чтобы просмотреть 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 получить информацию о проекте, который содержит эту страницу / представление?(Думаю нет, но кто знает ...)

Ответы [ 5 ]

50 голосов
/ 20 июля 2011

Кажется, это надежный и простой способ получить «запись» или основную сборку для веб-приложения.

Если вы поместите контроллеры в отдельный проект, вы можете обнаружить, что базовый класс 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;
    }
10 голосов
/ 26 октября 2016

В моем случае мне нужно было получить «сборку ввода» для веб-приложения до инициализации System.Web.HttpContext.Current.ApplicationInstance. Кроме того, мой код должен был работать для различных типов приложений (оконные службы, настольные приложения и т. Д.), И я не люблю загрязнять свой общий код проблемами Интернета.

Я создал собственный атрибут уровня сборки, который можно объявить в файле AssembyInfo.cs сборки, которую вы хотите назначить точкой сборки. Затем вы просто вызываете статический метод GetEntryAssembly атрибута, чтобы получить сборку записи. Если Assembly.GetEntryAssembly возвращает ненулевое значение, которое используется, в противном случае выполняется поиск в загруженных сборках сборки с пользовательским атрибутом. Результат кэшируется в 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();
        }
    }
}
4 голосов
/ 04 июля 2011

Как ответ на мой собственный вопрос (некоторые люди здесь очень обидчивы по принятой ставке) => Я не нашел лучшего способа, чем код, приведенный в вопросе.идеально, но это работает, пока ваша базовая страница определена во внешней сборке.

3 голосов
/ 25 ноября 2015

Алгоритм, предложенный в вопросе, действительно работал для меня, тогда как метод, использующий 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.Для этого типа приложения HttpContext.Current.Handler является модулем кода, содержащим точку входа текущего обработчика запроса.Handler.GetType (). Assembly - это временная сборка ASP.Net, но Handler.GetType (). BaseType.Assembly - это настоящая «входная сборка» моего приложения.Мне любопытно, работает ли то же самое для различных других типов приложений ASP.Net.

0 голосов
/ 15 апреля 2016

Единственный способ добиться того, чтобы это работало согласованно для веб-приложений (по крайней мере, в .NET 4.5.1), - это выполнить 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;
...