Get web application assembly name, regardless of current executing assembly - c#

Is is possible to get the assembly name of an ASP.NET web application, from a referenced assembly??
Assembly.GetEntryAssembly worked fine in desktop and console apps but it seems to be always null in web apps, and GetExecuting\GetCallingAssebly return my referenced assembly, not the one from the web app.
Long explanation:
I wrote a custom Settings Provider, that instead of reading configuration from the app config file, it gets the settings from a centralized configuration service.
The custom provider is in a separate assembly so it can be used by the different applications.
The ApplicationName property needs to be overriden with the app assembly name.
The way to use the provider is though a .net custom attribute, so I can't send any params to it.
Since non of the Assembly.Get*Assembly methods seem to work, the only thing a I can think of is requiring an appSetting with the app name for web apps, but I'm not really happy with that. Any help with this is appreciated, thanks!

Try
BuildManager.GetGlobalAsaxType().BaseType.Assembly

You can use
HttpContext.Current.ApplicationInstance.GetType().Assembly

I know this is an old question but this was my approach to a somewhat similar situation. In my case a was using another assembly for formatting a string with the version to show for multiple programs that have the same core.
Version v = null;
var a = Assembly.GetEntryAssembly() ?? GetWebEntryAssembly() ?? Assembly.GetExecutingAssembly();
SnapshotVersion = FileVersionInfo.GetVersionInfo(a.Location).ProductVersion;
if (ApplicationDeployment.IsNetworkDeployed)
{
var d = ApplicationDeployment.CurrentDeployment;
v = d.CurrentVersion;
v = new Version(v.Major, v.Minor, v.Revision);
}
else
v = a.GetName().Version;
if (v != null)
version = string.Format("{0}.{1}.{2}", v.Major, v.Minor, v.Build);
Because this is in a static constructor all I needed to do was to call any property of this static class from the Web Application and then find the last calling assembly that is different from the assembly that the static class is on. This was achieve with the method GetWebEntryAssembly.
private static Assembly GetWebEntryAssembly()
{
var frames = new StackTrace().GetFrames();
var i = frames.FirstOrDefault(c => Assembly.GetAssembly(c.GetMethod().DeclaringType).FullName != Assembly.GetExecutingAssembly().FullName).GetMethod().DeclaringType;
return Assembly.GetAssembly(i);
}

Related

How to dynamically load and unload (reload) a .dll assembly

I'm developing a module for an external application, which is a dll that is loaded.
However in order to develop, you have to restart the application to see the results of your code.
We have built a piece of code that loads a dll dynamically from a startassembly:
startassembly
var dllfile = findHighestAssembly(); // this works but omitted for clarity
Assembly asm = Assembly.LoadFrom(dllFile);
Type type = asm.GetType("Test.Program");
MethodInfo methodInfo = type.GetMethod("Run");
object[] parametersArray = new object[] { };
var result = methodInfo.Invoke(methodInfo, parametersArray);
Effectively we have a solution with a startassembly which will be static and a test assembly which will be invoked dynamically, which allows us to swap the assembly during runtime.
The problem
This piece of code will load a new dll every time and search for the highest version at the end of the assembly name. e.g. test02.dll will be loaded instead of test01.dll, because the application locks both startassemly.dll as well as test01.dll. Now we have to edit the properties > assembly name all the time.
I want to build a new dll while the main application still runs. However for now I get the message
The process cannot access the file test.dll because it is being used
by another process
I have read that you can unload a .dll using AppDomains however the problem is that I don't know how to properly unload an AppDomain and where to do this.
The goal is to have to reload the new test.dll everytime the window is re-opened (by a button click from the main application).
You cannot unload a single assembly, but you can unload an Appdomain. This means you need to create an app domain and load the assembly in the App domain.
Exmaple:
var appDomain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup
{
ApplicationName = "MyAppDomain",
ShadowCopyFiles = "true",
PrivateBinPath = "MyAppDomainBin",
});
ShadowCopyFiles property will cause the .NET runtime to copy dlls in "MyAppDomainBin" folder to a cache location so as not to lock the files in that path. Instead the cached files are locked. For more information refer to article about Shadow Copying Assemblies
Now let's say you have an class you want to use in the assembly you want to unload. In your main app domain you call CreateInstanceAndUnwrap to get an instance of the object
_appDomain.CreateInstanceAndUnwrap("MyAssemblyName", "MyNameSpace.MyClass");
However, and this is very important, "Unwrap" part of CreateInstanceAndUnwrap will cause the assembly to be loaded in your main app domain if your class does not inherit from MarshalByRefObject. So basically you achieved nothing by creating an app domain.
To solve this problem, create a 3rd Assembly containing an Interface that is implemented by your class.
For example:
public interface IMyInterface
{
void DoSomething();
}
Then add reference to the assembly containing the interface in both your main application and your dynamically loaded assembly project. And have your class implement the interface, and inherit from MarshalByRefObject. Example:
public class MyClass : MarshalByRefObject, IMyInterface
{
public void DoSomething()
{
Console.WriteLine("Doing something.");
}
}
And to get a reference to your object:
var myObj = (IMyInterface)_appDomain.CreateInstanceAndUnwrap("MyAssemblyName", "MyNameSpace.MyClass");
Now you can call methods on your object, and .NET Runtime will use Remoting to forward the call to the other domain. It will use Serialization to serialize the parameters and return values to and from both domains. So make sure your classes used in parameters and return values are marked with [Serializable] Attribute. Or they can inherit from MarshalByRefObject in which case the you are passing a reference cross domains.
To have your application monitor changes to the folder, you can setup a FileSystemWatcher to monitor changes to the folder "MyAppDomainBin"
var watcher = new FileSystemWatcher(Path.GetFullPath(Path.Combine(".", "MyAppDomainBin")))
{
NotifyFilter = NotifyFilters.LastWrite,
};
watcher.EnableRaisingEvents = true;
watcher.Changed += Folder_Changed;
And in the Folder_Changed handler unload the appdomain and reload it again
private static async void Watcher_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine("Folder changed");
AppDomain.Unload(_appDomain);
_appDomain = AppDomain.CreateDomain("MyAppDomain", null, new AppDomainSetup
{
ApplicationName = "MyAppDomain",
ShadowCopyFiles = "true",
PrivateBinPath = "MyAppDomainBin",
});
}
Then when you replace your DLL, in "MyAppDomainBin" folder, your application domain will be unloaded, and a new one will be created. Your old object references will be invalid (since they reference objects in an unloaded app domain), and you will need to create new ones.
A final note: AppDomains and .NET Remoting are not supported in .NET Core or future versions of .NET (.NET 5+). In those version, separation is achieved by creating separate processes instead of app domains. And using some sort of messaging library to communicate between processes.
Not the way forward in .NET Core 3 and .NET 5+
Some of the answers here assume working with .NET Framework. In .NET Core 3 and .NET 5+, the correct way to load assemblies (with ability to unload them) in the same process is with AssemblyLoadContext. Using AppDomain as a way to isolate assemblies is strictly for .NET Framework.
.NET Core 3 and 5+, give you two possible ways to load dynamic assemblies (and potentially unload):
Load another process and load your dynamic assemblies there. Then use an IPC messaging system of your choosing to send messages between the processes.
Use AssemblyLoadContext to load them in the same process. Note that the scope does NOT provide any kind of security isolation or boundaries within the process. In other words, code loaded in a separate context is still able to invoke other code in other contexts within the same process. If you want to isolate the code because you expect to be loading assemblies that you can't fully trust, then you need to load it in a completely separate process and rely on IPC.
An article explaining AssemblyLoadContext is here.
Plugin unloadability discussed here.
Many people who want to dynamically load DLLs are interested in the Plugin pattern. The MSDN actually covers this particular implementation here:
https://learn.microsoft.com/en-us/dotnet/core/tutorials/creating-app-with-plugin-support
2021-9-12 UPDATE
Off-the-Shelf Library for Plugins
I use the following library for plugin loading. It has worked extremely well for me:
https://github.com/natemcmaster/DotNetCorePlugins
what you're trying to do in the code you posted is unload the default app domain which your program will run in if another isn't specified. What you're probably wanting is to load a new app domain, load the assembly into that new app domain, and then unloaded the new app domain when the user destroys the page.
https://learn.microsoft.com/en-us/dotnet/api/system.appdomain?view=netframework-4.7
the reference page above should give you a working example of all of this.
Here is an example for loading and unloading an AppDomain.
In my example I have 2 Dll's: DynDll.dll and DynDll1.dll.
Both Dll's have the same class DynDll.Class and a method Run (MarshalByRefObject is required):
public class Class : MarshalByRefObject
{
public int Run()
{
return 1; //DynDll1 return 2
}
}
Now you can create a dynamic AppDomain and load a Assembly:
AppDomain loDynamicDomain = null;
try
{
//FullPath to the Assembly
string lsAssemblyPath = string.Empty;
if (this.mbLoad1)
lsAssemblyPath = Path.Combine(Application.StartupPath, "DynDll1.dll");
else
lsAssemblyPath = Path.Combine(Application.StartupPath, "DynDll.dll");
this.mbLoad1 = !this.mbLoad1;
//Create a new Domain
loDynamicDomain = AppDomain.CreateDomain("DynamicDomain");
//Load an Assembly and create an instance DynDll.Class
//CreateInstanceFromAndUnwrap needs the FullPath to your Assembly
object loDynClass = loDynamicDomain.CreateInstanceFromAndUnwrap(lsAssemblyPath, "DynDll.Class");
//Methode Info Run
MethodInfo loMethodInfo = loDynClass.GetType().GetMethod("Run");
//Call Run from the instance
int lnNumber = (int)loMethodInfo.Invoke(loDynClass, new object[] { });
Console.WriteLine(lnNumber.ToString());
}
finally
{
if (loDynamicDomain != null)
AppDomain.Unload(loDynamicDomain);
}
Here is an idea, instead of loading the DDL directly (as is), let the application rename it, then load the renamed ddl (e.g. test01_active.dll). Then, just check for the original file (test01.dll) before loading the assembly and if exists, just delete the current one(test01_active.dll) and then rename the updated version then reload it, and so on.
Here is a code shows the idea :
const string assemblyDirectoryPath = "C:\\bin";
const string assemblyFileNameSuffix = "_active";
var assemblyCurrentFileName = "test01_active.dll";
var assemblyOriginalFileName = "test01.dll";
var originalFilePath = Path.Combine(assemblyDirectoryPath, assemblyOriginalFileName);
var currentFilePath = Path.Combine(assemblyDirectoryPath, assemblyCurrentFileName);
if(File.Exists(originalFilePath))
{
File.Delete(currentFilePath);
File.Move(originalFilePath, currentFilePath);
}
Assembly asm = Assembly.LoadFrom(currentFilePath);
Type type = asm.GetType("Test.Program");
MethodInfo methodInfo = type.GetMethod("Run");
object[] parametersArray = new object[] { };
var result = methodInfo.Invoke(methodInfo, parametersArray);

dotnet core get a list of loaded assemblies for dependency injection

I'm using AutoFac to automatically register dependencies based on their interface implementations like so:
builder.RegisterAssemblyTypes(Assembly.GetEntryAssembly()).AsImplementedInterfaces();
This works great for the entry assembly, but what about all of the related assemblies?
I'd like to do something like:
IList<Assembly> assemblies = GetLoadedAssemblies();
foreach(var assembly in assemblies)
{
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
}
I've searched and see a bunch of .netcore 1.0 stuff with AssemblyLoadContext, etc., but that no longer seems to exist in 1.1. Basically, when you search, you get lots of outdated references to stuff that no longer works.
There's got to be a way to get the currently loaded assemblies.
How can I do this?
In .NET Core 2.0 Lots of APIs were brought from .NET Framework, among them is AppDomain.CurrentDomain.GetAssemblies() which allows you to get the assemblies that have been loaded into the execution context of application domain. Of course the concept wasn't fully ported from .NET Framework to .NET Core so you can't create your own app domains and treat them as unit of isolation inside single process (so you only have one app domain). But this is enough for what you want to achieve.
You can use Scrutor which is working with .netstandard 1.6 or take a look how they're doing it here.
public IImplementationTypeSelector FromAssemblyDependencies(Assembly assembly)
{
Preconditions.NotNull(assembly, nameof(assembly));
var assemblies = new List<Assembly> { assembly };
try
{
var dependencyNames = assembly.GetReferencedAssemblies();
foreach (var dependencyName in dependencyNames)
{
try
{
// Try to load the referenced assembly...
assemblies.Add(Assembly.Load(dependencyName));
}
catch
{
// Failed to load assembly. Skip it.
}
}
return InternalFromAssemblies(assemblies);
}
catch
{
return InternalFromAssemblies(assemblies);
}
}
This is an old question , but none of the answers works for me.
I use autofac with asp.net core webapi project (.net 6).
This is where I need to get assemblies:
host
// autofac
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
// autofac container
.ConfigureContainer<ContainerBuilder>(builder => {
// get assemblies for register
var assemblies = ???
builder.RegisterAssemblyTypes(assemblies).AsImplementedInterfaces()...
});
The project is desinged by DDD, dependcies are like this:
webapi -> domain,infra
infra -> domain
For my situation:
AppDomain.CurrentDomain.GetAssemblies() can only get webapi assembly.
BTW: Assembly.GetEntryAssembly() can get the webapi assebmly too.
webapiAssembly.GetReferencedAssemblies() can only get domain project's assembly. Can't find the infra assembly.
Why GetReferencedAssemblies() doest not get all of the referenced assemblies as expected?
It's because in my project, webapi uses an repository interface, which is defines in domain, but implements in infra. Webapi project doesn't make any direct dependcy with infra layer (use class/interface defined in infra). So the infra assembly is not included.
If webapi has a directly dependcy with infra, use GetReferencedAssemblies() would be just fine.
Here is another solution that works for me:
Use DependencyContext which is inclued in Microsoft.Extensions.DependencyModel (nuget package) to get all assemblies that I wanted (webapi, domain, infra).
public static class AssemblyHelper
{
// get all assemblies that I wanted
// you may want to filter assemblies with a namespace start name
public static Assembly[] GetAllAssemblies(string namespaceStartName = "")
{
var ctx = DependencyContext.Default;
var names = ctx.RuntimeLibraries.SelectMany(rl => rl.GetDefaultAssemblyNames(ctx));
if (!string.IsNullOrWhiteSpace(namespaceStartName))
{
if (namespaceStartName.Substring(namespaceStartName.Length - 1, 1) != ".")
namespaceStartName += ".";
names = names.Where(x => x.FullName != null && x.FullName.StartsWith(namespaceStartName)).ToArray();
}
var assemblies = new List<Assembly>();
foreach (var name in names)
{
try
{
assemblies.Add(Assembly.Load(name));
}
catch { } // skip
}
return assemblies.ToArray();
}
}

Determining .Net Framework at runtime from a library

I'm somewhat new to .Net, so go easy on me ;-). Anyway.
I working on my first WP7 library project which I hope will be compatible with both XNA and SilverLight applications. Based on whether I'm in XNA or Silverlight one of my factory classes needs to load different config class. Whats the best way to determine this at runtime, from a library.
I know I could do this with the "SILVERLIGHT+WINDOWS_PHONE" preprocessor directives at compile time. But that would mean building two DLLs, which isn't ideal.
~Sniff
I suspect that the information you're looking for can be found in the Environment.Version property or in the OperatingSystem.Version property.
The best I could think of is setting up your library like so:
[Conditional(#XNA),
Conditional(#WINDOWS_PHONE)]
public void DoSomeWork()
{
var x = null;
x = DoSomeXNAWork();
x = DoSomeWP7Work();
if (x != null)
{
...
}
}
[Conditional(#XNA)]
private ?? DoSomeXNAWork()
{
return ??;
}
[Conditional(#WINDOWS_PHONE)]
private ?? DoSomeWP7Work()
{
return ??;
}
Then, just make sure the project referencing this library has the directive set. Kind of like how Microsoft uses the Debug conditional classes such Debug.WriteLine(...). I'm not sure how you could get it to use 2 different config files. I'm sure there is a way because when you create a new Web Project (ASP.NET) there is a config file that is split into Web.Debug.config and Web.Release.config. I couldn't find an answer as to how to do it outside of ASP.NET though.

Accessing information in the AssemblyInfo.cs using Reflection in a Web Site

I have created a DLL that will gather information from the AssemblyInfo.cs. In the class constructor I am using Reflection to get the top-most application that is running.
public class AppInfo()
{
public AppInfo()
{
System.Reflection.Assembly assembly =
System.Reflection.Assembly.GetEntryAssembly();
if (assembly == null)
assembly = System.Reflection.Assembly.GetCallingAssembly();
//code to gather needed information
}
}
How this is intended to be used is if I call this from any DLL in a given application, MyApp, that lets say the name will always be 'MyApp'. Retrieving that information is not a problem and it works great in Windows Services and Windows Applications. My question is this:
How do I get the Assembly of the top-most Website?
I have found a few articles and I can get the information in the Global.asax.cs by moving the AssemblyInfo.cs for the Website out of the App_Code folder and into the root of the Website. Then by adding a compilerOption to the physical path of the AssemblyInfo.cs
<compiler
language="c#;cs;csharp"
extension=".cs"
compilerOptions="C:\Sandbox\MyWebSite\AssemblyInfo.cs"
type="Microsoft.CSharp.CSharpCodeProvider,System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" warningLevel="4">
Using that I am able to retrieve information in the AssemblyInfo.cs for the Website through System.Reflection.Assembly.GetExecutingAssembly(). Now I can overload the constructor of my AppInfo class to accept an Assembly and retrieve information that way, but if another DLL that is used by MyWebSite creates a new AppInfo() I will get the assembly information of that DLL instead of the parent Website.
I know that if I was working with Web Apps instead of Web Sites I wouldn't have this issue, but for reasons I won't go into I am not able to use Web Apps. Any suggestions on how I can read information from the AssemblyInfo.cs of the Website I'm running in no matter what DLL I'm in?
EDIT: I need this to work for Web Sites, Windows Apps and Windows Services
If I understand you properly, the problem is that Assembly.GetEntryAssembly() returns null in a Website and Assembly.GetCallingAssembly() is returning the wrong thing because you've got a chain of calls resulting in the website not being the immediate caller. If that's the case, you could find the "Entry Assembly" using the stack trace & walking back up the calling frames. The stack will be full of references from System.Web, etc as the call will have originated from deep within IIS somewhere, but you should be able to pick out the assembly you're interested in by grabbing the lowest frame that you can positively identify as belonging to you. Note that this is pretty hacky, but I think it'll get you what you want...
var trace = new StackTrace();
Assembly entryAssembly = null;
foreach (var frame in trace.GetFrames())
{
var assembly = frame.GetMethod().DeclaringType.Assembly;
//check if the assembly is one you own & therefore could be your logical
//"entry assembly". You could do this by checking the prefix of the
//Assembly Name if you use some standardised naming convention, or perhaps
//looking at the AssemblyCompanyAttribute, etc
if ("assembly is one of mine")
{
entryAssembly = assembly;
}
}
Hopefully someone else will be able to come up with a less nasty way of doing it... but if you're really stuck, this might help.
AppDomains are created per website and then the bin*.dll files are loaded into that Application's AppDomain. There is not a "Master Assembly" of the site, at least any that has any special meaning.
It is a process. System.Web.Hosting.ProcessHost which creates a System.Web.ApplicationHost through a combination of the System.Web.Hosting.ApplicationManger and System.Web.Hosting.Environment which then creates a System.AppDomain for the application.
AppDomain is secured pretty well so unless you know how to get into an AppDomain, you're out of luck.
If you can update your question, someone may be able to help more.
You MAY be able to do this if security is not setup properly"
String binDir = Server.MapPath("/bin/");
Response.Write(String.Format("Bin dir:{0}<br/><br/>",binDir));
foreach (string file in Directory.GetFiles(binDir, "*.dll"))
{
Response.Write(String.Format("File:{0}<br/>", file));
try
{
Assembly assembly = Assembly.LoadFile(file);
object[] attrinutes = assembly.GetCustomAttributes(true);
foreach (var o in attrinutes)
{
//AssemblyCompanyAttribute is set in the AssemblyInfo.cs
if (o is AssemblyCompanyAttribute)
{
Response.Write(String.Format("Company Attribute: Company = {0}<br/>", ((AssemblyCompanyAttribute)o).Company));
continue;
}
Response.Write(String.Format("Attribute: {0}<br/>", o));
}
}
catch(Exception ex)
{
Response.Write(String.Format("Exception Reading File: {0} Exception: {1}",file,ex));
}
}
You are assuming alot here that the Master site is not compiled (has a App_Code directory) and resides in the same Application Pool as your child site. Do you have needed access to do this? Is the master site a shared hosting site or something?
Deviant is right. But if you can you should use Assembly.Load instead of Assembly.LoadFile.
Here is some nice reference:
http://gisresearch.blogspot.com/2007/09/assembly-load-loadfrom-loadfile.html
Using Alconja's suggestion I was checking for assembly.GlobalAssemblyCache until I found the first one being there. The last one not being there is the first of my own dlls that I consider the entry assembly.
The class below helps getting a Type object if only the assembly name and the type name is known.
public static class AssemblyHandler {
private static Dictionary<String, Assembly> Assemblies;
public static Assembly GetAssembly(String Name) {
if (Assemblies == null) {
Assemblies = new Dictionary<string, Assembly>();
var mainAsm = Assembly.GetEntryAssembly();
if (mainAsm == null) {
var trace = new StackTrace();
foreach (var frame in trace.GetFrames()) {
var assembly = frame.GetMethod().DeclaringType.Assembly;
if (assembly.GlobalAssemblyCache) {
break;
}
mainAsm = assembly;
}
}
Assemblies.Add(mainAsm.FullName, mainAsm);
ScanReferencedAssemblies(mainAsm);
}
if (!Assemblies.ContainsKey(Name)) {
return null;
}
return Assemblies[Name];
}
private static void ScanReferencedAssemblies(Assembly Asm) {
foreach (var refAsmName in Asm.GetReferencedAssemblies()) {
Assembly a = Assembly.Load(refAsmName);
if (a.GlobalAssemblyCache) {
continue;
}
if (!Assemblies.ContainsKey(a.FullName)) {
Assemblies.Add(a.FullName, a);
ScanReferencedAssemblies(a);
}
}
}
public static Type GetType(string AssemblyName, string TypeName) {
return GetAssembly(AssemblyName).GetType(TypeName);
}
}

Using the Web Application version number from an assembly (ASP.NET/C#)

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.
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.
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.
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.
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() %>
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;
}
}
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
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!
Version version = new Version(Application.ProductVersion);
string message = version.ToString();
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();
}
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.
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 :)
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; }

Categories

Resources