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
Related
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.
Suppose I have version 1 of a strong-named assembly Interfaces.dll, which contains an IPlugin interface:
public interface IPlugin
{
void InstallSelf(IPluginManager pluginManager);
}
A number of plugins have been implemented and everything runs smoothly.
One day, it's decided that plugins probably ought to have names so we can see which plugins we have loaded. So we produce version 2 of Interfaces.dll containing:
public interface IPlugin
{
string PluginName { get; }
void InstallSelf(IPluginManager pluginManager);
}
Because I can't expect all the plugin implementors to immediately update their plugins, I'd like to create an adapter which adds a PluginName of "Unknown" to old plugin versions. Is there a way to implement such an adapter? I'd like it to look like this:
public sealed class PluginAdapter_v1_v2 : IPlugin[v2]
{
private readonly IPlugin[v1] _plugin;
public PluginAdapter_v1_v2(IPlugin[v1] plugin)
{
_plugin = plugin;
}
public string PluginName => "Unknown";
public void InstallSelf(IPluginManager pluginManager)
{
_plugin.InstallSelf(pluginManager);
}
}
But because they're both called IPlugin, this isn't going to work. An alternative would be to rename IPlugin to IPlugin_v1 and IPlugin_v2, or do a similar renaming of its namespace, but this would require the interface (or namespace) to be renamed every time there's a new version of Interfaces.dll, and would require implementors to change all references to the interface (or namespace) on updating to the latest assembly version. So, the question again:
Is it possible to implement an adapter of this sort, without renaming IPlugin or its namespace?
There is a method for doing this, but it's fairly long winded. By the end of this process, we'll end up an adapter class from IPlugin [v1] to IPlugin [v2].
Reference both versions of Interfaces.dll
Create a project in which to put the adapter class.
Add a reference to the latest version in Visual Studio as normal.
Right click on the project and select "Unload Project"
Right click on the project again and select "Edit [projectname].csproj"
Find the section that looks like:
<Reference Include="Interfaces">
<HintPath>path\to\library\v2\Interfaces.dll</HintPath>
</Reference>
and replace it with:
<Reference Include="Interfaces_v1">
<HintPath>path\to\library\v1\Interfaces.dll</HintPath>
<Aliases>Interfaces_v1</Aliases>
</Reference>
<Reference Include="Interfaces_v2">
<HintPath>path\to\library\v2\Interfaces.dll</HintPath>
<Aliases>Interfaces_v2</Aliases>
</Reference>
Save and close the project file
Right click on the project and select "Reload Project"
Reference both versions of IPlugin
Adding the Aliases elements into the project file means that types are no longer imported into the global namespace, but are instead imported into the Interfaces_v1 and Interfaces_v2 namespaces. This means that we can access the two IPlugin versions by different names:
At the top of the adapter source file (before any using statements), add the following:
extern alias Interfaces_v1;
extern alias Interfaces_v2;
Optionally, to neaten up the code a bit, add some type aliases too:
using IPlugin_v1 = Interfaces_v1::Interfaces.IPlugin;
using IPlugin_v2 = Interfaces_v2::Interfaces.IPlugin;
Implement the adapter
public sealed class PluginAdapter_v1_v2 : IPlugin_v2
{
private readonly IPlugin_v1 _pluginV1;
public PluginAdapter_v1_v2(IPlugin_v1 pluginV1)
{
_pluginV1 = pluginV1;
}
public string Name => _pluginV1.Name;
public string Version => "Unknown";
}
What you're asking for doesn't make sense. Adding PluginName to the interface would break all existing code that implements the interface. Your reason for not giving the new interface a new name or a new namespace is that you want to automatically use that new interface. Yet you don't want to break existing code. You can't have it both ways.
The simplest working approach is to have
public interface IPlugin
{
void InstallSelf(IPluginManager pluginManager);
}
public interface IPlugin2 : IPlugin
{
string PluginName { get; }
}
At some point, if you make a new release with breaking changes where existing plugins won't be supported any longer, you may then choose to merge the interfaces.
When a new version of your DLL is ready with a new interface definition, it includes all the older interface versions as well. All existing code would continue to compile and run. You can detect dynamically whether a plugin implementation implements only IPlugin, or also IPlugin2.
we are trying to hot swap (update) assemblies, the normal workflow is that we make some changes, build the assembly, do some changes and build again and in an ideal world the host app would get the new version of the assembly (with the updated types).
Here's our small plugin loader class:
public class PluginLoader<T>
{
private CompositionContainer _compositionContainer;
private RegistrationBuilder _registrationBuilder;
private DirectoryCatalog _catalog;
[ImportMany(AllowRecomposition = true)]
public IList<T> Plugins { get; set; }
public PluginLoader(string pluginsDirectory)
{
Plugins = new List<T>();
SetShadowCopy();
_registrationBuilder = new RegistrationBuilder();
_registrationBuilder
.ForTypesDerivedFrom(typeof(T))
.SetCreationPolicy(CreationPolicy.NonShared)
.Export<T>();
_catalog = new DirectoryCatalog(pluginsDirectory, _registrationBuilder);
_compositionContainer = new CompositionContainer(_catalog, CompositionOptions.DisableSilentRejection);
_compositionContainer.ComposeParts(this);
}
public void Reload()
{
_catalog.Refresh();
_compositionContainer.ComposeParts(this);
}
private static void SetShadowCopy()
{
AppDomain.CurrentDomain.SetShadowCopyFiles();
AppDomain.CurrentDomain.SetCachePath(Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "ShadowCopyCache"));
AppDomain.CurrentDomain.SetShadowCopyPath(Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "Plugins"));
}
}
We have code to recognize a new plugin dropping into the plugins folder using FileSystemWatcher, and we call Reload when that happens, but the new versions of assemblies aren't actually loaded.
Any pointers?
Notes:
No new or deleted types are recognized, it's as if it doesn't recognize the new assembly at all.
We checked and there are no composition and other errors either, so we are a bit lost :D
It is important to note that if we build the same non recognized assembly with a different compiler(Roslyn), then it is recognized (which points to nothing being badly setup, just that the assembly needs to be somehow different )
The methods called in SetShadowCopy are deprecated. You cannot enable ShadowCopy on an existing AppDomain. For an example on how to enable ShadowCopy on a new AppDomain, Have a look at this answer.
DirectoryCatalog.Refresh does update already loaded assemblies. It only checks for file deletions and additions. Have a look at this answer for a crude work-around. Note thought that I'm not sure if such an approach is thread-safe or production-ready since I have only tested simple scenarios. Another approach would be to create your own DirectoryCatalog that can handle updates as well. The MEF source code is available (as it is for the rest of the framework). The tricky part is thread-safety since the DirectoryCatalog implementation is using Microsoft's internal classes for locking.
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've an API DLL (API.dll, for example) which, in addition to many other thinks, makes available an abstract class (AbstractClass).
Now making use of that AbstractClass I've implemented it on two different dlls:
First.API.Implementation.dll with ConcreteImplementation1
Second.API.Implementation.dll with ConcreteImplementation2
Both ConcreteImplementation1 and ConcreteImplementation2 are implementation of the same abstract class.
What I want is an application where I can choose which of those two dlls to use and, through that, choose which implementation to use without the user having to change anything within the code and, if possible, without stopping the application.
Some configuration file where I can bring the application to use whatever implementation I want. Something like:
<appconfiguration>
<implementation_to_use>
<dll>First.API.Implementation.dll</dll>
<class>ConcreteImplementation1</class>
</implementation_to_use>
</appconfiguration>
I know near to nothing about dependency injection, apart from its concept, but I guess thats the perfect fit for this task.
I've researched several DI/IoC libraries but I'm not familiar with all the concepts and names. I can use whatever library I want. For what I can say these are the most used: StructureMap, Ninject and Sprint.NET
Moreover, apart from all the dlls and implementation I need to indicate a file to be used by that application. Can I indicate its path in that same file?
I just need some tips and directions to implement such a thing. Some examples using one of those libraries, would be awesome.
Thanks.
To get you started using StructureMap, create a console application, include in it:
structuremap.config:
<?xml version="1.0" encoding="utf-8" ?>
<StructureMap MementoStyle="Attribute">
<DefaultInstance
PluginType="DemoIoC.AbstractBase,DemoIoC"
PluggedType="DemoIoC.ConcreteImplementation1,DemoIoC"
Scope="Singleton" />
</StructureMap>
The PluginType and PluggedType attributes are "FullyQualifiedClassName,AssemblyName"
By default it will look for assemblies in the executable folder, I'm not sure how you would specify another location for the assemblies
There are plenty of options for Scope, e.g. Singleton, Transient, etc
Program.cs:
namespace DemoIoC
{
using System;
using StructureMap;
public static class Program
{
public static void Main(string[] args)
{
// here you initialize structuremap from the config file.
// You could probably use a FileSystemWatcher to reinitialize
// whenever the structuremap.config file changes
ObjectFactory.Initialize(x =>
{
x.UseDefaultStructureMapConfigFile = true;
});
var concrete = ObjectFactory.GetInstance<AbstractBase>();
concrete.Method1();
Console.ReadKey(true);
}
}
}
AbstractBase.cs:
namespace DemoIoC
{
public abstract class AbstractBase
{
public abstract void Method1();
}
}
ConcreteImplementation1.cs:
namespace DemoIoC
{
using System;
public class ConcreteImplementation1 : AbstractBase
{
public override void Method1()
{
Console.WriteLine("Called ConcreteImplementation1");
}
}
}