My goal is to execute some "piece of code", which uses certain assembly, over multiple versions of that assembly. The way I'm doing this is by executing that "piece of code" on separate AppDomains, one for each assembly version.
I was able to do this only when the "piece of code" uses the assembly through a reflection, but what I would like is to have that "piece of code" written in strongly typed manner.
In other words, let's say I have the following assembly:
namespace ClassLibrary1
{
public class Class1
{
internal const string Version = "1.0.0.0";
public string Method1() { return Version; }
}
}
Also it has the following definition in AssemblyInfo.cs:
[assembly: AssemblyVersion(ClassLibrary1.Class1.Version)]
Now let's say I have a "Versions" folder in which I have multiple versions of that assembly, for example:
/Versions/
├─ /1000/
│ └─ ClassLibrary1.dll
├─ /1001/
│ └─ ClassLibrary1.dll
└─ /1002/
└─ ClassLibrary1.dll
Now to execute the "piece of code" I'm using the following console application:
class Program
{
static void PieceOfCode(Assembly assembly)
{
Type class1Type = assembly.GetType("ClassLibrary1.Class1");
dynamic class1 = Activator.CreateInstance(class1Type);
string vesion = class1.Method1();
Console.WriteLine(vesion);
}
public sealed class SeparateDomainExecutor : MarshalByRefObject
{
public void Execute(Action<Assembly> action, string assemblyPath)
{
action(Assembly.LoadFrom(assemblyPath));
}
}
static void Main(string[] args)
{
foreach (string file in Directory.EnumerateFiles(#"C:\Versions", "*.dll", SearchOption.AllDirectories))
{
AppDomain domain = AppDomain.CreateDomain("ClassLibrary1 Domain");
var type = typeof(SeparateDomainExecutor);
var runner = (SeparateDomainExecutor)domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);
runner.Execute(PieceOfCode, file);
AppDomain.Unload(domain);
}
Console.Read();
}
}
The console application works fine, but I would like to replace that reflection usage in "PieceOfCode" with something like the following:
static void PieceOfCode()
{
ClassLibrary1.Class1 class1 = new ClassLibrary1.Class1();
Console.WriteLine(class1.Method1());
}
Is this possible?
The problem I have with this is that the PieceOfCode would be written using some specific version of ClassLibrary1 (probably the latest) and I don't see how I could "override" that version in seperate AppDomain.
I tried few things, but I always end up with FileLoadException.
Unfortunately when you write ClassLibrary1.Class1 in a statically typed piece of code, you need an assembly reference and the compiler uses that reference to name a given version of the class. Although the fully qualified names (typeof(Class1).AssemblyQualifiedName) do not contain the path or filename of the assembly, just the assembly name and version, the class loader has further limitations as you might notice:
it cannot load 2 classes from different assemblies with the same name into the same namespace
since path or filename is not part of assembly references, you cannot reference 2 assemblies compile-time with the same strong name
The way you use Assembly.LoadFrom(...) and dynamic binding is the best I could come up with. And this is how different versions of Office assemblies are usually treated from applications that integrate them.
The only possible solution I see is to separate piece of code into a separate assembly (say, MyStaticIntegration.dll) compile it against each version of your dependency (ClassLibrary1.dll) separately and then integrate each version of MyStaticIntegration.dll into your application the same way as you did it with ClassLibrary1.dll before.
The same wall-of-dynamic will remain in your application, but you could narrow down the interface you are using dynamically with this trick.
Related
I am trying to get the executing assembly version in C# 3.0 using the following code:
var assemblyFullName = Assembly.GetExecutingAssembly().FullName;
var version = assemblyFullName .Split(',')[1].Split('=')[1];
Is there another proper way of doing so?
Two options... regardless of application type you can always invoke:
Assembly.GetExecutingAssembly().GetName().Version
If a Windows Forms application, you can always access via application if looking specifically for product version.
Application.ProductVersion
Using GetExecutingAssembly for an assembly reference is not always an option. As such, I personally find it useful to create a static helper class in projects where I may need to reference the underlying assembly or assembly version:
// A sample assembly reference class that would exist in the `Core` project.
public static class CoreAssembly
{
public static readonly Assembly Reference = typeof(CoreAssembly).Assembly;
public static readonly Version Version = Reference.GetName().Version;
}
Then I can cleanly reference CoreAssembly.Version in my code as required.
In MSDN, Assembly.GetExecutingAssembly Method, is remark about method "getexecutingassembly", that for performance reasons, you should call this method only when you do not know at design time what assembly is currently executing.
The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly.
The following example illustrates:
using System;
using System.Reflection;
public class Example
{
public static void Main()
{
Console.WriteLine("The version of the currently executing assembly is: {0}",
typeof(Example).Assembly.GetName().Version);
}
}
/* This example produces output similar to the following:
The version of the currently executing assembly is: 1.1.0.0
Of course this is very similar to the answer with helper class "public static class CoreAssembly", but, if you know at least one type of executing assembly, it isn't mandatory to create a helper class, and it saves your time.
using System.Reflection;
{
string version = Assembly.GetEntryAssembly().GetName().Version.ToString();
}
Remarks from MSDN http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getentryassembly%28v=vs.110%29.aspx:
The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application. For example, if an unmanaged application creates an instance of a COM component written in C#, a call to the GetEntryAssembly method from the C# component returns null, because the entry point for the process was unmanaged code rather than a managed assembly.
Product Version may be preferred if you're using versioning via GitVersion or other versioning software.
To get this from within your class library you can call System.Diagnostics.FileVersionInfo.ProductVersion:
using System.Diagnostics;
using System.Reflection;
//...
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var productVersion = FileVersionInfo.GetVersionInfo(assemblyLocation).ProductVersion
This should do:
Assembly assem = Assembly.GetExecutingAssembly();
AssemblyName aName = assem.GetName();
return aName.Version.ToString();
I finally settled on typeof(MyClass).GetTypeInfo().Assembly.GetName().Version for a netstandard1.6 app. All of the other proposed answers presented a partial solution. This is the only thing that got me exactly what I needed.
Sourced from a combination of places:
https://msdn.microsoft.com/en-us/library/x4cw969y(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/2exyydhb(v=vs.110).aspx
Let's say I have 3 DLLs (BlueCar, RedCar, YellowCar) that each have a named class (BlueCarClass, etc) that also all implement the same interface, Car, and are all built from the same namespace (Car_Choices). So a DLL looks something like this before compiled:
namespace Car_Choices
{
public interface Car
{
void What_Color();
}
public class BlueCarClass : Car
{
public void What_Color()
{
MessageBox.Show('The color is blue.');
}
}
}
And the DLL's name would be "BlueCar.dll".
In the main program, the user selects which ever car color they want, and based on their choice it dynamically loads only the appropriate DLL and runs What_Color(). The main program has a copy of the Car interface. Right now I have the following, but it's not working.
static void Main()
{
string car_choice = win_form_list.ToArray()[0]; //gets choice from a drop down
Assembly car_assembly = Assembly.Load(car_choice); //car_choice is BlueCar
Type car_type = car_assembly.GetType("Car");
Car car = Activator.CreateInstance(type) as Car;
car.What_Color();
}
I've also tried
static void Main()
{
string car_choice = win_form_list.ToArray()[0]; //gets choice from a drop down
ObjectHandle car_handle = Activator.CreateInstance(assembly_name, "Car_Choices."+ car_choice);
Car car= (Car)handle.Unwrap();
car.What_Color();
}
Any help? Are there structural changes I need to make (such as putting each car color DLL in it's own namespace)? Or am I not understanding how to load and use classes from DLLs appropriately.
EDIT: Here's the solution that I got to work, in case anyone is looking for a more detailed answer.
PROJECT 1: The shared interface (as a Class library)
Car_Interface.cs
namespace Car_Interface
{
public interface ICar_Interface
{
char Start_Car();
}
}
Compile into Car_Interface.dll, reference DLL in next 2 projects.
PROJECT 2: Car interface implementation, as a class library
BlueCar.cs
namespace BlueCar_PlugIn
{
public class BlueCar : Car_Interface.ICar_Interface
{
public char Start_Car()
{
MessageBox.Show("Car is started");
}
}
}
Compile into BlueCar_PlugIn.dll
PROJECT 3: Main program/driver
Program.cs
namespace Main_Program
{
public class Program
{
static void Main()
{
Assembly assembly = Assembly.Load(DLL_name); //Where DLL_name is the DLL you want to load, such as BlueCar_PlugIn.dll
Type type = (Type)assembly.GetTypes().GetValue(0); //Class that implements the interface should be first. A resource type could also possibly be found
//OR
Type type = (Type)assembly.GetType(DLL_name + class_name); //In this case, BlueCar_PlugIn.BlueCar
Car_Interface.ICar_Interface new_car = (Car_Interface.ICar_Interface)Activator.CreateInstance(type);
new_car.Start_Car();
}
}
}
Now if you move both DLLs into bin (or where ever your program is compiled to) and run it, it'll be able to dynamically load BlueCar_PlugIn.dll but not neccessarily need it to run (ex, if you have YellowCar_PlugIn.dll and also RedCar_PlugIn.dll with similar implementations, only one will need to be loaded for the program to work).
Your code does not work because, for example, BlueCarClass does not implement Car that is in the application assembly because the fully qualified name of base classes are different.
The class Car that is in your BlueCar assembly may have fully qualified name
BlueCar.Car, BlueCar, Version=1.0.0.0, Culture=neutral, PublicKey=null
but the class Car that is in your application has different fully qualified name, something like this
SomeApp.Car, SomeApp, Version=1.0.0.0, Culture=neutral, PublicKey=null
Even though you put the class to the same namespace, the full name is still different because it include assembly name.
There are many ways of how to achieve results that you want: you can use MEF or you can create something more lightweight by yourself.
Your solution needs to have at least 3 assemblies:
Interface library. That keeps ICar interface.
Plugin library. (in your case BlueCar, RedCar etc). It references the library #1.
Application. It explicitly references library #1 and dynamically uses #2.
PS Actually you can do this using 2 assemblies by merging #1 and #3 and make #2 reference #3 instead of #1. It will work, however its logically incorrect because you introduce cross references (implicitly). In my opinion this approach smells.
So here is my issue. I have a complex archetecture of interfaces and abstract classes that I am trying to load up via Assembly.LoadFrom("x.dll"). When certain types that have an interface implementation where the implementation is explicit in a base class are trying to be loaded, I am getting a TypeLoadException saying:
Method 'MyMethod' in type 'MyPart2DerivedType' from assembly 'MyPart2Assembly, version...' does not have an implementation. I am trying to understand why this is as I have gone through several articles and have even attempted to delete the obj files and dlls manually. Here are the references to what I have done so far:
Solution to TypeLoadException
TypeLoadException says 'no implementation', but it is implemented
Visual Studio Forumns: TypeLoadException
Private accessors and explicit interface implementation
So here is my example code:
//This is in project 1
public interface IFooPart1
{
void DoStuff();
}
//This is in project 2
public interface IFooPart2
{
void DoOtherStuff();
}
//This is in project 3
public interface IFooPart3: IFooPart1, IFooPart2
{
void DoEvenMoreStuff();
}
//This is in project 4
public abstract class MyBaseType: IFooPart1, IFooPart2
{
void IFooPart1.DoStuff()
{
DoStuffInternal();
}
void IFooPart2.DoOtherStuff()
{
DoOtherStuffInternal();
}
}
//This is in project 5
public class MyDerivedType: MyBaseType, IFooPart3
{
public void DoEvenMoreStuff()
{
//Logic here...
}
}
//Only has references to projects 1, 2, & 3 (only interfaces)
public class Program
{
void Main(params string[] args)
{
//Get the path to the actual dll
string assemblyDll = args[0];
//Gets the class name to load (full name, eg: MyNameSpace.MyDerivedType)
string classNameToLoad = args[1];
//This part works...
var fooAssembly = Assembly.LoadFrom(assemblyDll);
//Here we throw a TypeLoadException stating
// Method 'DoStuff' in type 'MyDerivedType' from assembly 'Project 5...' does
// not have an implementation.
Type myDerivedTypeExpected = Assembly.GetType(classNameToLoad);
}
}
Note: If I move the explicit implementation to MyDerivedType instead of MyBaseType it works... but I don't get why I would have to do that. Seems like I should be able to. This code is only an example, the actual code has a factory that returns the loaded class but only via the interface type. (eg: var myDerivedType = GetInstance();)
Okay for everyone that is interested in my stupid fix. Here was my problem:
Project6 (which was the console app) has PROJECT references to the other projects, not references to the dlls in the location that they are supposed to build to. The other projects actually were being built to a specific repository area. So, the console application was using it's own version of the dll's when it was trying to automatically load the dependancies. This evidently made some other type way down there that was being dynamically loaded to not be loaded because it was not in the same folder as the dlls that were there...
So in short, Assembly.LoadFrom might cause you to load an assembly twice, but .NET treats it like a different assembly!!! This may introduce some real odd errors when trying to dynamically load types!!!
Please learn from my frustration/mistake. Fiends don't let freinds DI alone (code review is key to catching this stupid stuff).
I've had a similar problem caused by one of my projects having a reference to an older version of a nuget package dependency. The older version didn't have an implementation for one of the methods.
I am getting started with developing an Excel-DNA addin using IronPython with some C# as a wrapper for the calls to IronPython. With the generous help of the Excel-DNA developer, I have worked through some of the initial kinks of getting a sample up and running, but now I am trying to debug the addin in SharpDevelop, and I'm running into some problems. As I'm completely new to most of this, I'm not really sure if it is an issue with SharpDevelop, .NET, Excel-DNA or IronPython.
I have created two projects in one solution, one is a C# class library. The other is a python class library. I setup the project to debug following a tutorial I found on a blog. I am able to step through the first few lines of C# code, so that is progress, but when I get to the following line:
pyEngine.Runtime.LoadAssembly(myclass);
I get an exception:
"Could not load file or assembly
'Microsoft.Dynamic, Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=31bf3856ad364e35' or
one of its dependencies. The located
assembly's manifest definition does
not match the assembly reference.
(Exception from HRESULT: 0x80131040)"
But I'm pretty sure I have added the Microsoft.Dynamic reference to my project. It is version 1.1.0.20. This is included in the IronPython distribution but also in another location on my computer. I have tried setting the reference to both, but they both have the same version number and appear to be the same file size. Neither one works. Do I need version 1.0.0.0 or am I doing something else wrong? I don't really understand why anything pyEngine (the ScriptEngine returned by Python.CreateEngine()) would try to load a different version than the one included with the distribution.
Code is below. Let me know if you need any other information.
MyAddin.cs
/*
Added these references all as Local Copies - probably not necessary?
System.Windows.Forms
Microsoft.CSharp
ExcelDna.Integration (from Excel-DNA distribution folder)
IronPython (from IronPython folder)
IronPython.Modules (from IronPython folder)
Microsoft.Dynamic (from IronPython folder)
Microsoft.Scripting (from IronPython folder)
Microsoft.Scripting.Metadata (from IronPython folder)
mscorlib (I don't really know why I added this, but it was one of the references in my IronPython class library)
MyClass (this is the reference to my IronPython class - I checked to see that it gets copied in every time I rebuild the solution and it does)
These were automatically added by SharpDevelop when I created the project.
System
System.Core
System.Windows.Forms
System.Xml
System.Xml.Linq
*/
using System;
using System.IO;
using System.Windows.Forms;
using ExcelDna.Integration;
using System.Reflection;
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
public class MyAddIn : IExcelAddIn
{
public void AutoOpen()
{
try
{
string xllDirectory = Path.GetDirectoryName(#"C:/Users/myname/Documents/SharpDevelop Projects/IronPythonExcelDNATest/MyAddIn/bin/Debug/");
string dllPath = Path.Combine(xllDirectory,"MyClass.dll");
Assembly myclass = Assembly.LoadFile(dllPath);
ScriptEngine pyEngine = Python.CreateEngine();
pyEngine.Runtime.LoadAssembly(myclass);
ScriptScope pyScope = pyEngine.Runtime.ImportModule("MyClass");
object myClass = pyEngine.Operations.Invoke(pyScope.GetVariable("MyClass"));
IronTest.AddSomeStuff = pyEngine.Operations.GetMember<Func<double, double,double>>(myClass, "AddSomeStuff");
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
public void AutoClose()
{
}
}
public class IronTest
{
public static Func<double, double, double> AddSomeStuff;
public static double TestIPAdd(double val1, double val2)
{
try
{
return AddSomeStuff(val1, val2);
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
return double.NaN;
}
}
}
MyClass.py
class MyClass:
def __init__(self):
pass
def AddSomeStuff(self,x,y):
return x + y
Your IronPython stuff probably needs to run under the .NET 4 runtime. To tell Excel-DNA to load .NET 4, you have to explicitly add a RuntimeVersion attribute in the main .dna file. Your .dna file will start with something like:
<DnaLibrary RuntimeVersion="v4.0"> ... </DnaLibrary>
The default behaviour, if the attribute is omitted, is to load the .NET 2.0 version of the runtime (which is also used by .NET 3.0 and 3.5).
It might be possible to host IronPython under the .NET 2.0 runtime, but then you need to deal with the DLR libraries yourself, whereas they are built-in and already installed with .NET 4.
I have a question about how the .NET framework (2.0) resolves dependent assemblies.
We're currently involved in a bit of a rewrite of a large ASP.NET application and various satellite executables. There are also some nagging problems with our foundation classes that we developed a new API to solve. So far this is a normal, albeit wide-reaching, update.
Our heirarchy is:
ASP.NET (aspx)
business logic (DLLs)
foundation classes (DLLs)
So ASP.NET doesn't throw a fit, some of the DLLs (specifically the foundation classes) have a redirection layer that contains the old namespaces/functions and forwards them to the new API. When we replaced the DLLs, ASP.NET picked them up fine (probably because it triggered a recompile).
Precompiled applications don't though, even though the same namespaces and classes are in both sets of DLLs. Even when the file is renamed, it complains about the assemblyname attribute being different (which it has to be by necessity). I know you can redirect to differnet versions of the same assembly, but is there any way to direct to a completely different assembly?
The alternatives are to recompile the applications (don't really want to because the applications themselves haven't changed) or recompile the old foundation DLL with stubs refering to the new foundation DLL (the new dummy DLL is file system clutter).
You want to move the types to a new assembly? You can do that with [TypeForwardedTo(..)].
If you originally have (AssemblyA):
namespace SomeNamespace {
public class SomeType {}
}
You can instead move that type into AssemblyB, and have a virtually empty AssemblyA which references AssemblyB and simply contains:
[assembly: TypeForwardedTo(typeof(SomeNamespace.SomeType))]
Then anything trying to load SomeNamespace.SomeType from AssemblyA actually gets the type from AssemblyB.
Most of the runtime respects this attribute... everything except WCF extensions. Which bit me ;-p Oh, and it isn't a fan of nested types...
//File: RKAPPLET.EXE
namespace RKAPPLET
{
using RKMFC;
public static class Program
{
public static void Main ()
{
RKMFC.API.DoSomething();
}
}
}
//File: RKMFC.DLL
namespace RKMFC
{
public static class API
{
public static void DoSomething ()
{
System.Windows.Forms.MessageBox.Show("MFC!")
}
}
}
//File: RKNET.DLL
namespace RKNET
{
public static class API
{
public static void DoSomethingElse ()
{
System.Windows.Forms.MessageBox.Show("NET!")
}
}
}
namespace RKMFC
{
public static class API
{
public static void DoSomething ()
{
RKNET.API.DoSomethingElse()
}
}
}
I want RKAPPLET.EXE, compiled with RKMFC.DLL, to find RKNET.DLL (which has a copy of everything in RKMFC.DLL and then some) without recompiling either RKAPPLET.EXE (to point to it) or RKMFC.DLL (to redirect types).
Did you try adding <assemblyBinding> setting to config file ?
http://msdn.microsoft.com/en-us/library/twy1dw1e.aspx