Related
ASP.NET has specicial application folders like App_Code which:
Contains source code for shared classes and business objects (for example, ..cs, and .vb files) that you want to compile as part of your application. In a dynamically compiled Web site project, ASP.NET compiles the code in the App_Code folder on the initial request to your application. Items in this folder are then recompiled when any changes are detected.
Problem is, I'm building a web application, not a dynamically compiled web site. But I'd love to be able to store config values directly in C#, rather than serve via an XML and have to read in during Application_Start and store in HttpContext.Current.Application
So I have the following code in /App_Code/Globals.cs:
namespace AppName.Globals
{
public static class Messages
{
public const string CodeNotFound = "The entered code was not found";
}
}
Which could be anywhere within the application like this:
string msg = AppName.Globals.Messages.CodeNotFound;
The goal is to be able to store any literals in a configurable area that can be updated without recompiling the entire application.
I can use the .cs file by setting its build action to compile, but doing so strips out App_Code/Globals.cs from my output.
Q: Is there a way to identify some parts of a project that should dynamically compile while allowing the rest of the project to be precompiled?
If I set the build action to content - the .cs file will get copied to the bin folder and compiled at runtime. However, in that case, it's not available at design time.
If I set the build action to compile - I can access the objects the same as any other compiled class during design/runtime, but it'll get stripped out of the /App_Code folder when published. I can still place it in the output directory via Copy Always, but the already compiled classes seem to take priority so I can't push config changes without re-deploying the whole application.
Problem Overview
We need to overcome two different problems here:
The first is having a single file that can be compiled at build time and also re-compiled at runtime.
The second is resolving the two different versions of that class created by the solving the first problem so we can actually make use of them.
Problem 1 - Schrödinger's Compilation
The first problem is trying to get a class that is both compiled and not compiled. We need to compile it at design time so that other sections of code are aware it exists and can use its properties with strong typing. But normally, compiled code is stripped out of the output so there aren't multiple versions of the same class causing naming conflicts.
In any case, we need to compile the class initially, but there are two options to persist a re-compilable copy:
Add the file to App_Code, which is compiled at runtime by default, but set it's Build Action = Compile so it's available at design time as well.
Add a regular class file, which is compiled at design time by default, but set it to Copy to Output Directory = Copy Always, so there's a chance we can evaluate it at runtime as well.
Problem 2 - Self Imposed DLL Hell
At a bare minimum, this is a tricky task to charge to the compiler. Any code that consumes a class, must have a guarantee that it exists at compile time. Anything that is dynamically compiled, whether via App_Code or otherwise, will be part of an entirely different assembly. So producing an identical class is treated more like a picture of that class. The underlying type might be the same, but ce n'est une pipe.
We have two options: use an interface or crosswalk between assemblies:
If we use an interface, we can compile it with the initial build and any dynamic types can implement that same interface. This way we are safely relying on something that exists at compile time, and our created class can be safely swapped out as a backing property.
If we cast types across assemblies, it's important to note that any existing usages rely on the type that was originally compiled. So we'll need to grab the values from the dynamic type and apply those property values to the original type.
Existing Answers
Per evk, I like the idea of querying AppDomain.CurrentDomain.GetAssemblies() on startup to check for any new assemblies/classes. I'll admit that using an interface is probably an advisable way to unify precompiled/dynamically compiled classes, but I would ideally like to have a single file/class that can simply be re-read if it changes.
Per S.Deepika, I like the idea of dynamically compiling from a file, but don't want to have to move the values to a separate project.
Ruling out App_Code
App_Code does unlock the ability to build two versions of the same class, but it's actually hard to modify either one after publication as we'll see. Any .cs file located in ~/App_Code/ will be dynamically compiled when the application runs. So in Visual Studio, we can build the same class twice by adding it to App_Code and setting the Build Action to Compile.
Build Action and Copy Output:
When we debug locally, all .cs files will be built into the project assembly and the physical file in ~/App_Code will also be built as well.
We can identify both types like this:
// have to return as object (not T), because we have two different classes
public List<(Assembly asm, object instance, bool isDynamic)> FindLoadedTypes<T>()
{
var matches = from asm in AppDomain.CurrentDomain.GetAssemblies()
from type in asm.GetTypes()
where type.FullName == typeof(T).FullName
select (asm,
instance: Activator.CreateInstance(type),
isDynamic: asm.GetCustomAttribute<GeneratedCodeAttribute>() != null);
return matches.ToList();
}
var loadedTypes = FindLoadedTypes<Apple>();
Compiled and Dynamic Types:
This is really close to solving problem #1. We have access to both types every time the app runs. We can use the compiled version at design time and any changes to the file itself will automatically be recompiled by IIS into a version that we can access at runtime.
The problem is apparent however once we step out of debug mode and try to publish the project. This solution relies on IIS building the App_Code.xxxx assembly dynamically, and that relies on the .cs file being inside the root App_Code folder. However, when a .cs file is compiled, it is automatically stripped out of the published project, to avoid the exact scenario we're trying to create (and delicately manage). If the file was left in, it would produce two identical classes, which would create naming conflicts whenever either one was used.
We can try to force its hand by both compiling the file into the project's assembly and also copying the file into the output directory. But App_Code doesn't work any of it's magic inside of ~/bin/App_Code/. It'll only work at the root level ~/App_Code/
App_Code Compilation Source:
With every publish, we could manually cut and paste the generated App_Code folder from the bin and place it back at the root level, but that's precarious at best. Perhaps we could automate that into build events, but we'll try something else...
Solution
Compile + (Copy to Output and Manually Compile File)
Let's avoid the App_Code folder because it will add some unintended consequences.
Just create a new folder named Config and add a class that will store the values we want to be able to modify dynamically:
~/Config/AppleValues.cs:
public class Apple
{
public string StemColor { get; set; } = "Brown";
public string LeafColor { get; set; } = "Green";
public string BodyColor { get; set; } = "Red";
}
Again, we'll want to go to the file properties (F4) and set to compile AND copy to output. This will give us a second version of the file we can use later.
We'll consume this class by using it within a static class that exposes the values from anywhere. This helps separate concerns, especially between the need to dynamically compile and statically access.
~/Config/GlobalConfig.cs:
public static class Global
{
// static constructor
static Global()
{
// sub out static property value
// TODO magic happens here - read in file, compile, and assign new values
Apple = new Apple();
}
public static Apple Apple { get; set; }
}
And we can use it like this:
var x = Global.Apple.BodyColor;
What we'll attempt to do inside the static constructor, is seed Apple with the values from our dynamic class. This method will be called once every time the application is restarted, and any changes to the bin folder will automatically trigger recycling the app pool.
In short order, here's what we'll want to accomplish inside of the constructor:
string fileName = HostingEnvironment.MapPath("~/bin/Config/AppleValues.cs");
var dynamicAsm = Utilities.BuildFileIntoAssembly(fileName);
var dynamicApple = Utilities.GetTypeFromAssembly(dynamicAsm, typeof(Apple).FullName);
var precompApple = new Apple();
var updatedApple = Utilities.CopyProperties(dynamicApple, precompApple);
// set static property
Apple = updatedApple;
fileName - The File path might be specific to where you'd like to deploy this, but note that inside of a static method, you need to use HostingEnvironment.MapPath instead of Server.MapPath
BuildFileIntoAssembly - In terms of loading the assembly from a file, I've adapted the code from the docs on CSharpCodeProvider and this question on How to load a class from a .cs file. Also, rather than fight dependencies, I just gave the compiler access to every assembly that was currently in the App Domain, same as it would have gotten on the original compilation. There's probably a way to do that with less overhead, but it's a one time cost so who cares.
CopyProperties - To map the new properties onto the old object, I've adapted the method in this question on how to Apply properties values from one object to another of the same type automatically? which will use reflection to break down both objects and iterate over each property.
Utilities.cs
Here's the full source code for the Utility methods from above
public static class Utilities
{
/// <summary>
/// Build File Into Assembly
/// </summary>
/// <param name="sourceName"></param>
/// <returns>https://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx</returns>
public static Assembly BuildFileIntoAssembly(String fileName)
{
if (!File.Exists(fileName))
throw new FileNotFoundException($"File '{fileName}' does not exist");
// Select the code provider based on the input file extension
FileInfo sourceFile = new FileInfo(fileName);
string providerName = sourceFile.Extension.ToUpper() == ".CS" ? "CSharp" :
sourceFile.Extension.ToUpper() == ".VB" ? "VisualBasic" : "";
if (providerName == "")
throw new ArgumentException("Source file must have a .cs or .vb extension");
CodeDomProvider provider = CodeDomProvider.CreateProvider(providerName);
CompilerParameters cp = new CompilerParameters();
// just add every currently loaded assembly:
// https://stackoverflow.com/a/1020547/1366033
var assemblies = from asm in AppDomain.CurrentDomain.GetAssemblies()
where !asm.IsDynamic
select asm.Location;
cp.ReferencedAssemblies.AddRange(assemblies.ToArray());
cp.GenerateExecutable = false; // Generate a class library
cp.GenerateInMemory = true; // Don't Save the assembly as a physical file.
cp.TreatWarningsAsErrors = false; // Set whether to treat all warnings as errors.
// Invoke compilation of the source file.
CompilerResults cr = provider.CompileAssemblyFromFile(cp, fileName);
if (cr.Errors.Count > 0)
throw new Exception("Errors compiling {0}. " +
string.Join(";", cr.Errors.Cast<CompilerError>().Select(x => x.ToString())));
return cr.CompiledAssembly;
}
// have to use FullName not full equality because different classes that look the same
public static object GetTypeFromAssembly(Assembly asm, String typeName)
{
var inst = from type in asm.GetTypes()
where type.FullName == typeName
select Activator.CreateInstance(type);
return inst.First();
}
/// <summary>
/// Extension for 'Object' that copies the properties to a destination object.
/// </summary>
/// <param name="source">The source</param>
/// <param name="target">The target</param>
/// <remarks>
/// https://stackoverflow.com/q/930433/1366033
/// </remarks>
public static T2 CopyProperties<T1, T2>(T1 source, T2 target)
{
// If any this null throw an exception
if (source == null || target == null)
throw new ArgumentNullException("Source or/and Destination Objects are null");
// Getting the Types of the objects
Type typeTar = target.GetType();
Type typeSrc = source.GetType();
// Collect all the valid properties to map
var results = from srcProp in typeSrc.GetProperties()
let targetProperty = typeTar.GetProperty(srcProp.Name)
where srcProp.CanRead
&& targetProperty != null
&& (targetProperty.GetSetMethod(true) != null && !targetProperty.GetSetMethod(true).IsPrivate)
&& (targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) == 0
&& targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType)
select (sourceProperty: srcProp, targetProperty: targetProperty);
//map the properties
foreach (var props in results)
{
props.targetProperty.SetValue(target, props.sourceProperty.GetValue(source, null), null);
}
return target;
}
}
But Why Tho?
Okay, so there are other more conventional ways to accomplish the same goal. Ideally, we'd shoot for Convention > Configuration. But this provides the absolute easiest, most flexible, strongly typed way to store config values I've ever seen.
Normally config values are read in via an XML in an equally odd process that relies on magic strings and weak typing. We have to call MapPath to get to the store of value and then do Object Relational Mapping from XML to C#. Instead here, we have the final type from the get go, and we can automate all of the ORM work between identical classes that just happen to be compiled against different assemblies.
In either case, the dream output of that process is to be able to write and consume C# directly. In this case, if I want to add an extra, fully configurable property, it's as easy as adding a property to the class. Done!
It will be available immediately and recompiled automatically if that value changes without needing to publish a new build of the app.
Dynamically Changing Class Demo:
Here's the full, working source code for the project:
Compiled Config - Github Source Code | Download Link
You can move configuration part to separate project, and create common interface like (IApplicationConfiguration.ReadConfiguration) to access it.
You can compile the code dynamically at run time like below, and you can access the configuration details using reflection.
public static Assembly CompileAssembly(string[] sourceFiles, string outputAssemblyPath)
{
var codeProvider = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = false,
IncludeDebugInformation = true,
OutputAssembly = outputAssemblyPath
};
// Add CSharpSimpleScripting.exe as a reference to Scripts.dll to expose interfaces
compilerParameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
var result = codeProvider.CompileAssemblyFromFile(compilerParameters, sourceFiles); // Compile
return result.CompiledAssembly;
}
Let's see how dynamic compilation of files in App_Code works. When first request to your application arrives, asp.net will compile code files in that folder into assembly (if were not compiled before), and then load that assembly into current application domain of asp.net application. That's why you see your message in a watch - assembly was compiled and is available in current app domain. Because it was compiled dynamically, of course you have compile-time error when trying to reference it explicitly - this code is not yet compiled, and when it will be compiled - it might have completely different structure and message you reference might just not be there at all. So there is no way you can explicitly reference code from dynamic-generated assembly.
What options do you have then? For example, you can have an interface for your messages:
// this interface is located in your main application code,
// not in App_Code folder
public interface IMessages {
string CodeNotFound { get; }
}
Then, in your App_Code file - implement that interface:
// this is in App_Code folder,
// you can reference code from main application here,
// such as IMessages interface
public class Messages : IMessages {
public string CodeNotFound
{
get { return "The entered code was not found"; }
}
}
And then in main application - provide a proxy by searching current app domain for assembly with type that implements IMessage interface (only once, then cache it) and proxy all calls to that type:
public static class Messages {
// Lazy - search of app domain will be performed only on first call
private static readonly Lazy<IMessages> _messages = new Lazy<IMessages>(FindMessagesType, true);
private static IMessages FindMessagesType() {
// search all types in current app domain
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
foreach (var type in asm.GetTypes()) {
if (type.GetInterfaces().Any(c => c == typeof(IMessages))) {
return (IMessages) Activator.CreateInstance(type);
}
}
}
throw new Exception("No implementations of IMessages interface were found");
}
// proxy to found instance
public static string CodeNotFound => _messages.Value.CodeNotFound;
}
This will achieve your goal - now when you change code in App_Code Messages class, on next request asp.net will tear down current application domain (first waiting for all pending requests to finish), then create new app domain, recompile your Messages and load into that new app domain (note that this recreating of app domain always happen when you change something in App_Code, not just in this particular situation). So next request will already see new value of your message without you explicitly recompile anything.
Note that you obviously cannot add or remove messages (or change their names) without recompiling main application, because doing that will require changes to IMessages interface which belongs to main application code. If you try - asp.net will throw compilation failure error on next (and all subsequent) requests.
I'd personally avoid doing such things, but if you are fine with that - why not.
I am using LoadFrom(), to load dlls, but for some reason this load function doesn't work on all dlls,
i want to load 3000 dlls to get from each one the copyright attribute.
my code :
class ReverseDLL
{
private Assembly assembly;
private AssemblyDescriptionAttribute desc;
private AssemblyTitleAttribute title;
private AssemblyCopyrightAttribute copyRight;
public string getCopyright(string path)
{
try
{
//assembly = System.Reflection.Assembly.Load(System.IO.File.ReadAllBytes(path));
assembly = System.Reflection.Assembly.LoadFrom(path);//"C:\\Windows\\winsxs\\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.1_none_bb1f6aa1308c35eb\\msvcm90d.dll");//path);// LoadFrom(path);
desc = (AssemblyDescriptionAttribute)
AssemblyDescriptionAttribute.GetCustomAttribute(
assembly, typeof(AssemblyDescriptionAttribute));
title = (AssemblyTitleAttribute)
AssemblyTitleAttribute.GetCustomAttribute(
assembly, typeof(AssemblyTitleAttribute));
copyRight = (AssemblyCopyrightAttribute)AssemblyCopyrightAttribute.GetCustomAttribute(assembly, typeof(AssemblyCopyrightAttribute));
}
catch
{
this.copyRight = new AssemblyCopyrightAttribute("");
}
if (this.copyRight == null)
this.copyRight = new AssemblyCopyrightAttribute("");
return copyRight.Copyright;
}
}
I don't know about the reflection problem without you providing more info (such as the error), but you could also try access the file itself:
string copyright = FileVersionInfo.GetVersionInfo(path).LegalCopyright;
This accesses the file-system meta-data (like you would see in explorer), and has the advantage of working for both managed and unmanaged dlls; but it requires that meta-data to exist (it doesn't look at the attribute).
Edit: a quick check indicates that (as expected) the compiler does check for this attribute and populate the file meta-data correctly.
Have you tried stopping on exceptions? Ctrl + Alt + E, stop on framework exceptions when they are thrown. The exception message should give you some information as to why the DLL couldn't be loaded.
Using reflection is not the optimal approach, as some of the dll may have dependencies you don't have.
Using a metadata parser can give you the things you want,
http://ccimetadata.codeplex.com/
http://www.mono-project.com/Cecil
The way Marc mentioned does not work for most .NET specific metadata.
We implement a plugin framework for our application and load plugin assemblies using Assembly.Loadfrom. We then use GetTypes() and further examine the types with each plugin file for supported Interfaces.
A path for the plugins is provided by the user and we cycle through each of the files in the folder to see if it (the plugin) supports our plugin interface. If it does, we create an instance, if not we move onto the next file.
We build two versions of software from the one code base (appA_1 and appA_2).
Loading the plugins works well when the plugins are loaded by the application that was built at the same time as the plugin file. However if we build appA_2 and point to the plugin folder of appA_1, we get an exception when GetTypes() is called.
A basic version of our code is;
var pluginAssembly = Assembly.LoadFrom(FileName);
foreach (var pluginType in pluginAssembly.GetTypes())
{
We get a "ReflectionTypeLoadException" exception.
This is concerning because we want our application to be able to load the types of any plugin, built by anyone. Is there something we are missing?
EDIT:
After iterating through the LoaderExceptions we have discovered that there is a single file libPublic.dll that generates a System.IO.FileNotFoundException exception. The strange thing is that this file resides in the application directory and the plugin is referenced to the project file.
EDIT 2:
In the exception log we find the following
"Comparing the assembly name resulted in the mismatch: Revision Number"
A few things:
Make sure you don't have duplicate assemblies in the plugin directory (i.e. assemblies that you're already loading in your main app from your app directory.) Otherwise, when you load your plugin, it may load an additional copy of the same assembly. This can lead to fun exceptions like:
Object (of type 'MyObject') is not of type 'MyObject'.
If you're getting the exception when instantiating a type, you may need to handle AppDomain.AssemblyResolve:
private void App_Startup(object sender, StartupEventArgs e)
{
// Since we'll be dynamically loading assemblies at runtime,
// we need to add an appropriate resolution path
// Otherwise weird things like failing to instantiate TypeConverters will happen
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
var domain = (AppDomain) sender;
foreach (var assembly in domain.GetAssemblies())
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
}
I realize it's a bit strange to have to tell the CLR that, in order to resolve an assembly, find the assembly with the name we're using to resolve, but I've seen odd things happen without it. For example, I could instantiate types from a plugin assembly, but if I tried to use TypeDescriptor.GetConverter, it wouldn't find the TypeConverter for the class, even though it could see the Converter attribute on the class.
Looking at your edits, this is probably not what's causing your current exception, though you may run into these issues later as you work with your plugins.
Thanks to this post I could solve the ReflectionTypeLoadException that I was getting in a UITypeEditor. It's a designer assembly (a winforms smart-tag used at design-time) of a custom class library, that scan for some types.
/// <summary>
/// Get the types defined in the RootComponent.
/// </summary>
private List<Type> getAssemblyTypes(IServiceProvider provider)
{
var types = new List<Type>();
try
{
IDesignerHost host = (IDesignerHost)provider.GetService(typeof(IDesignerHost));
ITypeResolutionService resolution = (ITypeResolutionService)provider.GetService(typeof(ITypeResolutionService));
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
foreach (var assembly in ((AppDomain)sender).GetAssemblies())
{
if (assembly.FullName == args.Name)
{
return assembly;
}
}
return null;
};
Type rootComponentType = resolution.GetType(host.RootComponentClassName, false);
types = rootComponentType.Assembly.GetTypes().ToList();
}
catch
{
}
return types;
}
You are getting an assembly version mismatch. Since your plugins refer to this libPublic.dll, you must version it carefully and in particular not bump its revision/build/etc. numbers at every compile.
I have a Winforms desktop application that is loading multiple MEF parts with the same Interface type.
Problem:
When I try to load more than one of the same type I get the following exception:
The composition remains unchanged. The changes were rejected because of the following error(s): The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.
1) No valid exports were found that match the constraint '((exportDefinition.ContractName = "BOCA.TaskPilot.Common.Extensions.IFolderViewExtension") && (exportDefinition.Metadata.ContainsKey("ExportTypeIdentity") && "BOCA.TaskPilot.Common.Extensions.IFolderViewExtension".Equals(exportDefinition.Metadata.get_Item("ExportTypeIdentity"))))', invalid exports may have been rejected.
Resulting in: Cannot set import 'TaskPilot.Windows.MainForm.FolderViewExtension (ContractName="BOCA.TaskPilot.Common.Extensions.IFolderViewExtension")' on part 'TaskPilot.Windows.MainForm'.
Element: TaskPilot.Windows.MainForm.FolderViewExtension (ContractName="BOCA.TaskPilot.Common.Extensions.IFolderViewExtension") --> TaskPilot.Windows.MainForm
Here is the code to load the parts:
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
//string myExecName = Assembly.GetExecutingAssembly().Location;
//string myPath = Path.GetDirectoryName(myExecName);
catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Data\TaskPilot\Development\Source\BOCA.TaskPilot.FolderView\bin\Debug"));
catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Data\TaskPilot\Development\Source\BOCA.TaskPilot.TaskView\bin\Debug"));
// Uncomment below line and it works without exceptions raised
//catalog.Catalogs.Add(new DirectoryCatalog(#"C:\Data\TaskPilot\Development\Source\BOCA.FileManager\bin\Debug"));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
Here's the code at the class for each of the MEF parts:
[Export(typeof(IFolderItemsViewExtension))
public partial class TaskTreeView : DevExpress.XtraEditors.XtraUserControl, IFolderItemsViewExtension, IPartImportsSatisfiedNotification]
Here's the Import used on the Main form:
[ImportMany(AllowRecomposition = true)]
private IEnumerable<IFolderItemsViewExtension> TaskViewExtensions = null;
If I uncomment the last Catalog.Catalogs.Add line it throws the exception. If I run it without that it runs just fine. That line loads a different user control that implements the IFolderItemsViewExtension Interface. I've tried to just load a dummy project that all it has is the user control and that interface and I still get the same exception. No matter what I do I still get this exception.
It seems that everything runs fine as long as I'm not loading more than one of the same type of MEF part export.
This is using the latest version of 2009.22.10.0 of the System.ComponentModel.Composistion from the MEF download.
The error indicates that it can't find an export of type IFolderViewExtension. Note that this is different from the import of IFolderItemsViewExtension you have shown.
My guess is that the problem is not that you have multiple IFolderItemsViewExtensions, but that you have multiple IFolderViewExtensions, or there is some other contract that you have more than one of that you are using with an import that requires exactly one.
This may be caused because you have the same assembly in more than one of your directory catalogs. It is easy for this to happen if you have a reference to an assembly and copy local is set to true.
I guess might have more than one export statement in your Export class.
I was facing the same issue and this solved when i removed all other expert statement from that export class. and now it is working fine.
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; }