Использование номера версии веб-приложения из сборки (ASP.NET/C#)

Как получить номер версии вызывающего веб-приложения в сборке, на которую ссылаются?

Я пытался использовать System.Reflection.Assembly.GetCallingAssembly().GetName(), но он просто дает мне динамически скомпилированную сборку (возвращая номер версии 0.0.0.0).

ОБНОВЛЕНИЕ. В моем случае мне нужно было решение, которое не требовало бы обратной ссылки на класс в сборке веб-приложения. Ответ Джейсона ниже (помеченный как принятый) соответствует этому требованию, а многие другие, представленные здесь, - нет.


person David Duffett    schedule 16.04.2009    source источник
comment
... Мне нужно решение, которое не требует ссылки на класс в сборке веб-приложения - мне было бы любопытно узнать, зачем вам это нужно. Должен сказать, я думаю, что решение Йодана Таубера выглядит для меня намного чище, чем принятый ответ, хотя я бы использовал typeof(Global), а не имя класса для конкретного приложения.   -  person Joe    schedule 04.01.2012
comment
@Joe - метод находится в общей сборке, используемой в различных решениях. Эти приложения могут быть веб-приложениями, а могут и не быть. В любом случае мне нужно иметь возможность получить номер версии приложения, фактически не ссылаясь на класс в приложении.   -  person David Duffett    schedule 04.01.2012
comment
@ Дэвид, я нахожу это запутанным и плохо сформулированным вопросом даже с вашим обновлением. Если выбранный вами ответ — это то, что вы действительно ищете, вопрос необходимо изменить, чтобы избежать путаницы. Вы должны включить требование о том, что вас может вызывать не веб-приложение, И вы ничего не знаете о типе HttpApplication (global.cs) или любом типе в сборке. Если это так, я бы подумал о рефакторинге этого интерфейса.   -  person Tom Deloford    schedule 05.01.2012


Ответы (12)


Вот некоторый код, который я использую, который поддерживает получение «основной» сборки приложения из веб-приложений или не-веб-приложений, затем вы можете использовать GetName().Version для получения версии.

Сначала он пытается использовать GetEntryAssembly() для не-веб-приложений. Это возвращает null в ASP.NET. Затем он просматривает HttpContext.Current, чтобы определить, является ли это веб-приложением. Затем он использует тип текущего HttpHandler, но сборка этого типа может быть сгенерированной сборкой ASP.NET, если вызов сделан со страницы ASPX, поэтому он проходит цепочку BaseType HttpHandler, пока не найдет тип, которого нет в пространство имен, которое ASP.NET использует для своих сгенерированных типов ("ASP"). Обычно это будет тип в вашей основной сборке (например, Страница в вашем файле кода программной части). Затем мы можем использовать сборку этого типа. Если ничего не помогает, вернитесь к GetExecutingAssembly().

У этого подхода все еще есть потенциальные проблемы, но он работает в наших приложениях.

    private const string AspNetNamespace = "ASP";

    private static Assembly getApplicationAssembly()
    {
        // Try the EntryAssembly, this doesn't work for ASP.NET classic pipeline (untested on integrated)
        Assembly ass = Assembly.GetEntryAssembly();

        // Look for web application assembly
        HttpContext ctx = HttpContext.Current;
        if (ctx != null)
            ass = getWebApplicationAssembly(ctx);

        // Fallback to executing assembly
        return ass ?? (Assembly.GetExecutingAssembly());
    }

    private static Assembly getWebApplicationAssembly(HttpContext context)
    {
        Guard.AgainstNullArgument(context);

        object app = context.ApplicationInstance;
        if (app == null) return null;

        Type type = app.GetType();
        while (type != null && type != typeof(object) && type.Namespace == AspNetNamespace)
            type = type.BaseType;

        return type.Assembly;
    }

ОБНОВЛЕНИЕ: я включил этот код в небольшой проект на GitHub и NuGet.

person Jason Duffett    schedule 26.01.2011
comment
Это ответ, который я искал - кажется, это единственный ответ, который фактически извлекает версию веб-приложения (а не версию сборки, в которой вы находитесь), не полагаясь на запись версии в файле конфигурации. Хороший. - person David Duffett; 26.01.2011
comment
Версия getWebApplicationAssembly у меня не работала из-за нулевого значения IHttpHandler. Я придумал версию с использованием httpContext.ApplicationInstance вместо context.CurrentHandler. - person Cristi Potlog; 06.06.2011
comment
Спасибо за совет, Кристи. Я предполагаю, что context.CurrentHandler будет работать только для потоков, непосредственно обслуживающих запросы asp.net, и только после создания IHttpHandler. - person Jason Duffett; 26.06.2011
comment
@Cristi: должен был сделать это ответом - person quentin-starin; 20.07.2011
comment
Даффман, мне любопытно, почему ответ @yodan не совсем попал в цель. - person toddkitta; 19.08.2011
comment
@toddkitta: ответ Йодана представит зависимость от упомянутой сборки BACK к классу в веб-приложении. Я бы предпочел решение, которое я могу использовать, которое не ссылается ни на какие классы в этом проекте. - person David Duffett; 05.09.2011
comment
@TomDeloford - Похоже, ваш ответ требует ссылки на класс в веб-сборке. Ответ Джейсона не требует этого. - person David Duffett; 04.01.2012
comment
@CristiPotlog Я обновил проект на GitHub, чтобы включить ваше изменение для использования httpContext.ApplicationInstance, поскольку CurrentHandler работает только в приложениях WebForms, а не MVC. - person Jason Duffett; 12.12.2012
comment
Какая сборка содержит метод Guard,AgainstNullArgument? Вроде не стандартный. - person CarlR; 23.11.2015
comment
Guard.AgainstNullArgument был удобным методом, который я использовал в этом проекте. Фактически это выглядит так: if (argument == null) throw new ArgumentNullException(Аргумент не может быть null); - person Jason Duffett; 15.12.2015

Я считаю, что самый простой однострочный способ получить версию вашей «основной» сборки (вместо динамической):

typeof(MyMainClass).Assembly.GetName().Version

Используйте свой класс верхнего уровня, который вряд ли когда-либо «изменит свое значение» или будет заменен в ходе рефакторинга, как MyMainClass. Вы знаете, в какой сборке определен этот самый класс, и больше не может быть путаницы относительно того, откуда берется номер версии.

person Yodan Tauber    schedule 02.11.2010
comment
+1, мне кажется, это очевидное и самое чистое решение, несмотря на обновление OP, в котором говорится, что ему нужно решение, которое не требует ссылки на класс в сборке веб-приложения (было бы полезно, если бы он сказал, почему). Если это проект веб-приложения ASP.NET, очевидным классом для использования будет Global, базовый класс кода программной части для Global.asax. - person Joe; 04.01.2012
comment
Единственная проблема заключается в том, что моя dll с кодом getversion не может получить доступ к dll верхнего уровня, которая содержит основной класс. - person Carra; 05.04.2013

Я предпочитаю Web.Config для хранения текущей версии сайта.

Вы также можете попробовать создать файл AssemblyInfo.cs в корневом каталоге веб-приложения, содержащий следующее:

using System.Reflection;
using System.Runtime.CompilerServices;
...
[assembly: AssemblyVersion("1.0.*")]
...

затем получите доступ к значению с помощью кода, подобного этому:

System.Reflection.Assembly.GetExecutingAssembly()

Вот еще информация в классе AssemblyInfo.

person Random Developer    schedule 16.04.2009
comment
Получить версию из фактического веб-приложения довольно просто, но это можно сделать только в коде - с помощью System.Reflection.GetExecutingAssembly().GetName().Version.ToString(). Я думаю, что есть какой-то способ получения это из ссылочной сборки, хотя... - person David Duffett; 16.04.2009
comment
@David: Вероятно, ты имеешь в виду System.Reflection.Assembly.GetExecutingAssembly()... :) - person abatishchev; 29.10.2011
comment
Сохранение версии приложения в файле web.config упрощает получение данных из скриптов вне выполнения кода. Я переключусь на web.config из атрибута AssemblyVersion. - person Alex; 15.04.2015

Чтобы добавить к ответчикам, которые уже разместили. Чтобы получить версию сборки в веб-приложении ASP.Net, вам необходимо поместить в код файла метод, аналогичный следующему:

protected string GetApplicationVersion() {
    return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
}

На странице ASPX вы хотите отобразить номер версии, просто поместив:

<%= GetApplicationVersion() %>
person Amir Khawaja    schedule 07.01.2010
comment
Спасибо, это именно то, что я хотел. - person mack; 12.06.2013

На всякий случай кто-то все еще заинтересован; это должно помочь и должно быть немного безопаснее, чем просто взять BaseType из ApplicationInstance, чтобы получить в свои руки реализацию Global.asax.

Global.asax всегда компилируется в ту же сборку, что и атрибуты сборки из AssemblyInfo.cs, поэтому это должно работать для всех веб-приложений, определяющих Global.asax.

Для тех, кто не определяет свой собственный Global.asax, он будет возвращаться к версии сгенерированного типа global_asax, который всегда равен 0.0.0.0, а для приложений, которые не являются веб-приложениями, он просто не вернет версию в все.

Бонус; использование класса BuildManager не требует активного экземпляра HttpContext, что означает, что вы также сможете использовать его из кода запуска приложения.

public static Version GetHttpApplicationVersion() {
  Type lBase = typeof(HttpApplication);
  Type lType = BuildManager.GetGlobalAsaxType();

  if (lBase.IsAssignableFrom(lType))
  {
    while (lType.BaseType != lBase) { lType = lType.BaseType; }
    return lType.Assembly.GetName().Version;
  }
  else
  {
    return null;
  }
}
person Ron Otten    schedule 27.08.2012
comment
Наконец-то что-то работает! Пробовал с помощью entryassembly, которая имеет значение null, используя httpcontext, который имеет значение null, и итерацию моей трассировки стека, которая также не сработала. - person Carra; 05.04.2013

HttpContext.Current.ApplicationInstance является производным от класса в файле global.asax.cs. Вы можете сделать следующее

 var instance = HttpContext.Current.ApplicationInstance;
 Assembly asm = instance.GetType().BaseType.Assembly;
 System.Version asmVersion = asm.GetName().Version;

Он работает как в ASP.NET (ASPX), так и в ASP.NET MVC.

person Andrei Schneider    schedule 24.01.2012

Я столкнулся с похожей проблемой и подумал, что вы можете найти решение полезным.

Мне нужно было сообщить о текущей версии приложения (проекта веб-приложения) из пользовательского серверного элемента управления, где серверный элемент управления содержался в другой библиотеке. Проблема заключалась в том, что самые "простые" геттеры сборки не обеспечивали правильной сборки.

  • Assembly.GetExecutingAssembly() вернул сборку, содержащую элемент управления; не сборка приложения.
  • Assembly.GetCallingAssembly() возвращал разные сборки в зависимости от того, где я находился в дереве вызовов; обычно System.Web, а иногда и сборка, содержащая элемент управления.
  • Assembly.GetEntryAssembly() вернулся null.
  • new StackTrace().GetFrames()[idx].GetMethod().DeclaringType.Assembly извлекает сборку фрейма в трассировке стека по индексу idx; однако, помимо того, что это неэстетично, дорого и подвержено ошибкам в расчетах индекса кадра, трассировка стека может не содержать каких-либо вызовов сборки приложения.
  • Assembly.GetAssembly(Page.GetType()) забил мне App_Web_@#$@#$%@ сборку, содержащую динамически сгенерированную страницу. Разумеется, динамическая страница наследует класс от сборки моего приложения, что и привело к окончательному решению:

Assembly.GetAssembly(Page.GetType().BaseType)

Имея в руках ссылку на сборку, вы можете перейти к версии по ее имени:

var version = Assembly.GetAssembly(Page.GetType().BaseType)
                      .GetName()
                      .Version;

Теперь это решение работает, потому что у меня была ссылка на тип из сборки приложения. Мы не используем никакие страницы, которые не наследуются от исходного кода, поэтому для нас это оказывается эффективным, но ваш пробег может отличаться, если методы кодирования в вашей организации отличаются.

Удачного кодирования!

person kbrimington    schedule 18.10.2010
comment
Упрощено до: (HttpContext.Current.ApplicationInstance).GetType().BaseType.Assembly.GetName.Version.ToString() - person Dave; 30.10.2018

Некоторая информация здесь: http://www.velocityreviews.com/forums/showpost.php?p=487050&postcount=8

в asp.net 2.0 каждая страница встроена в собственную сборку, поэтому только dll, в которую встроен AssemblyInfo.cs, вернет правильный ответ. просто добавьте статический метод в AssemblyInfo.cs, который возвращает информацию о версии, и вызовите этот метод с других ваших страниц.

-- Брюс (sqlwork.com)

Но я написал простой метод для этого:

    public static string GetSystemVersion(HttpServerUtility server)
    {
        System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
        doc.Load(server.MapPath("~/web.config"));
        System.Xml.XmlNamespaceManager ns = new System.Xml.XmlNamespaceManager(doc.NameTable);
        ns.AddNamespace("bla", "http://schemas.microsoft.com/.NetConfiguration/v2.0");

        System.Xml.XmlNode node = doc.SelectSingleNode("/bla:configuration/bla:system.web/bla:authentication/bla:forms[@name]", ns);

        string projectName = "";
        if (node != null && node.Attributes != null && node.Attributes.GetNamedItem("name") != null)
            projectName = node.Attributes.GetNamedItem("name").Value; //in my case, that value is identical to the project name (projetname.dll)
        else
            return "";

        Assembly assembly = Assembly.Load(projectName);
        return assembly.GetName().Version.ToString();
    }
person Seiti    schedule 10.06.2009

Если вы ищете это в веб-элементе управления, один из способов — найти тип страницы с выделенным кодом (т. е. класс, наследуемый от System.Web.UI.Page). Обычно это находится в веб-сборке потребителя.

Type current, last;
current = Page.GetType();
do
{
    last = current;
    current = current.BaseType;
} while (current != null && current != typeof(System.Web.UI.Page));
return last;

Я надеюсь, что есть лучший способ.

person Matt Woodard    schedule 25.09.2009

В вопросе указано без ссылки (экземпляры), о которых он (изначально) не говорил, не зная типов веб-приложений.

ИЗМЕНИТЬ OP пояснил, что да, они действительно не требуют знания типов в вызывающей веб-сборке, поэтому ответ уместен. Однако я бы серьезно подумал о рефакторинге такого решения, чтобы версия передавалась в другую сборку.

Для большинства людей в этом сценарии, если вы знаете пользовательский тип HttpApplication:

 typeof(MyHttpApplication).Assembly.GetName().Version

и если у вас есть только динамически сгенерированный тип:

 typeof(DynamiclyGeneratedTypeFromWebApp).BaseType.Assembly.GetName().Version

Перестаньте голосовать за этот ответ :)

person Tom Deloford    schedule 04.01.2012
comment
У вас есть жестко закодированные типы в этом коде, поэтому вам понадобится ссылка. Решение должно быть чем-то, что можно поместить в пакет NuGet и установить в любое веб-приложение (поэтому вы не можете использовать жестко закодированное приложение MyHttpApplication). Моя цель — создать повторно используемый экран-заставку, показывающий версию и название приложения (из AssemblyTitleAttribute). - person YipYip; 20.05.2015
comment
Как я уже сказал в своем ответе, вопрос гласит «без ссылки». Имя типа не является ссылкой, поэтому для многих людей (и как сформулирован вопрос) это самое простое решение. Нигде не упоминается об установке в каком-либо веб-приложении или пакете NuGet. - person Tom Deloford; 20.05.2015
comment
Я думаю, что причина, по которой вопрос не требует ссылки, заключается в том, что код должен существовать в отдельной библиотеке, которая не должна иметь знаний во время компиляции о веб-приложениях, в которых он будет использоваться (сегодня MyHttpApplication, завтра AnotherHttpApplication). Если вы скопируете и вставите код непосредственно в веб-приложение (и жестко закодируете тип), то, конечно, у вас будет доступ к типам, и вам не понадобится ссылка. - person YipYip; 29.05.2015

Итак, мне пришлось получить сборку из ссылочной dll.

В мире asp.NET MVC/WebAPI всегда будет хотя бы один класс, наследуемый от System.Web.HttpWebApplication. Реализация ниже ищет этот класс.

using System;
using System.Linq;

static Assembly GetWebAssembly() => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetExportedTypes().Any(t => t.BaseType?.FullName == "System.Web.HttpApplication"));

Вышеупомянутое использует System.Linq, чтобы найти эту связь, но это также может быть реализовано без.

Сначала получаем все загруженные сборки

AppDomain.CurrentDomain.GetAssemblies()

Затем перечислите IEnumerable<Assembly> и получите все типы, расположенные непосредственно в сборке.

a.GetExportedTypes()

Затем посмотрите, не наследуется ли какой-либо из типов от System.Web.HttpWebApplication

t.BaseType?.FullName == "System.Web.HttpApplication"

В моей реализации я гарантировал, что этот код будет вызываться только один раз, но если это не гарантируется, я бы сильно обернул это в Lazy<T> или другую кэшированную реализацию отложенной загрузки, поскольку продолжать выполнять слепой поиск довольно дорого.

using System;
using System.Linq;

// original method
private static Assembly GetWebAssembly() => AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetExportedTypes().Any(t => t.BaseType?.FullName == "System.Web.HttpApplication"));

// lazy load implementation
private static Lazy<Assembly> _webAssembly = new Lazy<Assembly>(GetWebAssembly);
public static Assembly WebAssembly { get => _webAssembly.Value; }
person Kom N    schedule 28.08.2020

person    schedule
comment
Это относится к веб-приложениям или только к приложениям Windows Forms? - person Simon Tewsi; 28.04.2014