56

How do I obtain the version number of the calling web application in a referenced assembly?

I've tried using System.Reflection.Assembly.GetCallingAssembly().GetName() but it just gives me the dynamically compiled assembly (returning a version number of 0.0.0.0).

UPDATE: In my case I needed a solution that did not require a reference back to a class within the web application assembly. Jason's answer below (marked as accepted) fulfils this requirement - a lot of others submitted here don't.

David Duffett
  • 3,145
  • 2
  • 26
  • 27
  • "... I needed a solution that did not require a reference back to a class within the web application assembly" - I'd be curious to know why you require this. I must say I think Yodan Tauber's solution looks a lot cleaner to me than the accepted answer - though I'd use `typeof(Global)` rather than an application-specific class name. – Joe Jan 04 '12 at 16:00
  • 2
    @Joe - The method is in a shared assembly used in various solutions. Those applications may or may not be web applications. Either way, I need to be able to obtain the version number of the application, without actually referencing a class within the application. – David Duffett Jan 04 '12 at 17:17
  • @David I find this a confusing and poorly phrased question even with your update. If the answer you selected is what youre really looking for then the question needs amending to avoid confusion. You should include the requirement that you could be being called by a non web app AND you have no knowledge of the HttpApplication (global.cs) type or any type within the assembly. If this is the case I would consider refactoring this interface. – Tom Deloford Jan 05 '12 at 16:57

12 Answers12

53

Here is some code I use that supports getting the application's "main" assembly from either Web or non-web apps, you can then use GetName().Version to get the version.

It first tries GetEntryAssembly() for non-web apps. This returns null under ASP.NET. It then looks at HttpContext.Current to determine if this is a web application. It then uses the Type of the current HttpHandler - but this type's assembly might be a generated ASP.NET assembly if the call is made from with an ASPX page, so it traverses the HttpHandler's BaseType chain until it finds a type that isn't in the namespace that ASP.NET uses for its generated types ("ASP"). This will usually be a type in your main assembly (eg. The Page in your code-behind file). We can then use the Assembly of that Type. If all else fails then fall back to GetExecutingAssembly().

There are still potential problems with this approach but it works in our applications.

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

UPDATE: I've rolled this code up into a small project on GitHub and NuGet.

Jason Duffett
  • 3,428
  • 2
  • 23
  • 23
  • This is the answer I was looking for - it seems to be the only answer that actually retrieves the version of the "Web Application" (rather than the version of the assembly you are in) without relying on recording the version in a config file. Nice. – David Duffett Jan 26 '11 at 10:26
  • 7
    The version of `getWebApplicationAssembly` didn't worked for me 'cause of an null `IHttpHandler`. I came up with a version using `httpContext.ApplicationInstance` instead of `context.CurrentHandler`. – Cristi Potlog Jun 06 '11 at 14:29
  • Thanks for the tip Cristi. I guess context.CurrentHandler will only work for threads directly servicing asp.net requests, and only after the IHttpHandler has been created. – Jason Duffett Jun 26 '11 at 11:24
  • @Cristi: should have made that an answer – quentin-starin Jul 19 '11 at 20:44
  • Duffman, I'm curious as to why @yodan 's answer didn't quite hit the mark for you. – toddkitta Aug 19 '11 at 18:46
  • 3
    @toddkitta: Yodan's answer would introduce a dependency from the referenced assembly BACK to a class in the web application. I would rather a solution that I can use that does not reference any classes within that project. – David Duffett Sep 05 '11 at 11:18
  • @TomDeloford - Looks like your answer requires a reference back to a class within the web assembly. Jason's answer doesn't require that. – David Duffett Jan 04 '12 at 14:11
  • @CristiPotlog I've updated the project on GitHub to include your change to use httpContext.ApplicationInstance as CurrentHandler only works in WebForms, not MVC, applications. – Jason Duffett Dec 12 '12 at 11:35
  • Which assembly contains the Guard,AgainstNullArgument method? It doesn't appear to be a standard one. – CarlR Nov 23 '15 at 13:26
  • Guard.AgainstNullArgument was a convenience method I was using in that project. It effectively looks like this: if (argument == null) throw new ArgumentNullException("Argument cannot be null"); – Jason Duffett Dec 15 '15 at 16:18
43

I find that the simplest one-liner way to get the version of your "main" assembly (instead of the dynamic one) is:

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

Use your top-level class, which isn't likely to ever "change its meaning" or to be replaced as part of a refactoring effort, as MyMainClass. You know in which assembly this very class is defined and there can no longer be confusion as to where the version number comes from.

Yodan Tauber
  • 3,907
  • 2
  • 27
  • 48
  • 1
    +1, this seems to me to be the obvious and cleanest solution, despite the OP's update stating that he wants a solution that does not require a reference to a class within the web application assembly (it would help if he said why). If it's an ASP.NET Web Application project, the obvious class to use would be `Global`, the code-behind base class for Global.asax. – Joe Jan 04 '12 at 14:22
  • The only problem with this is that my dll with the getversion code can't access the top level dll that contains the mainclass. – Carra Apr 05 '13 at 07:16
27

I prefer the Web.Config to store the current version of the site.

You can also try create an AssemblyInfo.cs file in the web application root that has the following:

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

then access the value via the code like this:

System.Reflection.Assembly.GetExecutingAssembly()

Here is more informaiton on the AssemblyInfo class.

Random Developer
  • 1,334
  • 2
  • 12
  • 31
  • 2
    Retrieving the version from the actual web application is fairly easy, but can only be done in code behind - using System.Reflection.GetExecutingAssembly().GetName().Version.ToString().I would think there would be some way of retrieving it from a referenced assembly though... – David Duffett Apr 16 '09 at 13:12
  • 1
    @David: Probably you mean `System.Reflection.Assembly.GetExecutingAssembly()...` :) – abatishchev Oct 28 '11 at 20:04
  • Storing application version in web.config makes it easy to retrieve from scripts outside of executing code. I'll be switching to web.config from AssemblyVersion attribute – Alex Apr 15 '15 at 17:34
14

To add to the responders that have already posted. In order to get the assembly version in an ASP.Net web application you need to place a method in the code behind file similar to:

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

In the ASPX page you want to display the version number simply place:

<%= GetApplicationVersion() %>
Amir Khawaja
  • 459
  • 1
  • 6
  • 13
8

Just in case anyone is still interested; this should do the trick and should be a tad safer than just taking the BaseType of ApplicationInstance to get your hands on the Global.asax implementation.

Global.asax is always compiled into the same assembly as the assembly attributes from AssemblyInfo.cs, so this should work for all web applications that define a Global.asax.

For those that don't define their own Global.asax, it will fall back to the version of the generated global_asax type, which is always 0.0.0.0, and for applications that aren't web applications, it will just return no version at all.

Bonus; using the BuildManager class does not require an active HttpContext instance, which means you should be able to use this from application startup code as well.

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;
  }
}
Amal Dev
  • 1,938
  • 1
  • 14
  • 26
Ron Otten
  • 81
  • 1
  • 1
  • Finally, something that works! Tried by using the entryassembly which is null, using the httpcontext which is null and iterating my stacktrace which didn't work either. – Carra Apr 05 '13 at 08:01
6

HttpContext.Current.ApplicationInstance is derived from the class in the global.asax.cs. You can do the following

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

It works both in ASP.NET (ASPX) and ASP.NET MVC

Andrei Schneider
  • 3,618
  • 1
  • 33
  • 41
4

I encountered a similar problem, and thought you might find the solution useful.

I needed to report the current application version (of a web application project) from a custom server control, where the server control was contained in a different library. The problem was that the "easiest" assembly getters did not provide the right assembly.

  • Assembly.GetExecutingAssembly() returned the assembly containing the control; not the application assembly.
  • Assembly.GetCallingAssembly() returned different assemblies depending on where I was at in the call tree; usually System.Web, and sometimes the assembly containing the control.
  • Assembly.GetEntryAssembly() returned null.
  • new StackTrace().GetFrames()[idx].GetMethod().DeclaringType.Assembly retrieves the assembly of a frame in the stack trace at index idx; however, besides being inelegant, expensive, and prone to miscalculation on the frame index, it is possible for the stack trace to not contain any calls to the application assembly.
  • Assembly.GetAssembly(Page.GetType()) scored me the App_Web_@#$@#$%@ assembly containing the dynamically generated page. Of course, the dynamic page inherits a class from my application assembly, so that led to the final solution:

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

With the assembly reference in hand, you can drill to the version through its name:

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

Now, this solution works because I had a reference to a type from the application assembly. We don't use any pages that do not inherit from a code behind, so it happens to be effective for us, but your mileage may vary if your organization's coding practices are different.

Happy coding!

kbrimington
  • 25,142
  • 5
  • 62
  • 74
  • Simplified to:(HttpContext.Current.ApplicationInstance).GetType().BaseType.Assembly.GetName.Version.ToString() – Dave Oct 30 '18 at 02:58
2
Version version = new Version(Application.ProductVersion);
string message = version.ToString();
Rob
  • 45,296
  • 24
  • 122
  • 150
noname
  • 21
  • 1
1

If you are looking for this from a web control, one hack is to find the type of the code-behind Page (ie. the class that inherits from System.Web.UI.Page). This is normally in the consumer's web assembly.

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

I hope there is a better way.

Matt Woodard
  • 1,956
  • 1
  • 16
  • 13
1

The question states with no reference (instances) it did not (originally) say with no knowledge of web application types.

EDIT the OP clarified to state that yes they do really require no knowledge of types within the calling web assembly, so the answer is appropriate. However I would seriously consider refactoring such a solution such that the version is passed into the other assembly.

For most people in this scenario if you know the custom HttpApplication type:

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

and if you only have a dynamic generated type:

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

Stop voting me down for this answer :)

Tom Deloford
  • 2,055
  • 1
  • 23
  • 24
  • You have hard-coded types in that code, so you'll need a reference. The solution should be something that can be put into a NuGet package and installed into any web application (and therefore, you can't use a hard-coded "MyHttpApplication"). My purpose is to create a reusable splash screen that shows app version and title (from AssemblyTitleAttribute). – YipYip May 19 '15 at 22:46
  • As I stated in my answer the question states 'with no reference'. A type name is not a reference, therefore for a lot of people (and as the question is phrased) this is the easiest solution. No where does it mention being installed in any web application or a NuGet package. – Tom Deloford May 20 '15 at 08:47
  • I think the reason the question asks for "no reference" is because the code must exist in a separate library that must have no compile-time knowledge of the web applications it will be used in (today "MyHttpApplication", tomorrow "AnotherHttpApplication"). If you copy & paste the code directly into the web application (and hard code the type), then of course you have access to the types and you wouldn't need a reference. – YipYip May 28 '15 at 21:57
1

Some info here: http://www.velocityreviews.com/forums/showpost.php?p=487050&postcount=8

in asp.net 2.0 each page is built into it own assembly, so only the dll the AssemblyInfo.cs is built into will return the correct answer. just add a static method to AssemblyInfo.cs that returns the version info, and call this method from your other pages.

-- bruce (sqlwork.com)

But I wrote a simple method to do that:

    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();
    }
Seiti
  • 2,138
  • 2
  • 21
  • 33
0

So, I had to get the Assembly from a referenced dll.

In the asp.NET MVC/WebAPI world, there is always going to be at least one class which inherits from System.Web.HttpWebApplication. The implementation below searches for that class.

using System;
using System.Linq;

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

The above uses System.Linq in order to find that relationship, but this can also be implemented without.

First, we get all loaded assemblies

AppDomain.CurrentDomain.GetAssemblies()

Then, enumerate through the IEnumerable<Assembly>, and get all of the types directly located in the assembly.

a.GetExportedTypes()

Then, see if any of the types inherit from System.Web.HttpWebApplication

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

In my implementation, I ensured this code would only be called once, but if that is not guaranteed, I'd highly wrapping this in a Lazy<T> or other cached lazy load implementation as it is rather expensive to keep performing the blind search.

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; }
Kom N
  • 63
  • 4