In my situation there are three components: Consumer class, IExposedIface interface and Exposed class implementing IExposedIface. Both Consumer and Exposed are linked statically with IExposedIface, but Consumer bears no compile-time reference to Exposed.
I am trying to come up with a scheme which would allow Consumer loading different versions of Exposed at runtime (depending on input data - let's say each input document carries an information about which version of Exposed should be used to process it). To achieve this, I started studying AppDomains and now I have a basic version working.
So far it seems to me there are two options when it comes to providing IExposedIface assembly to Exposed assembly.
Having IExposedIface.dll only in Consumer's bin directory and handling AppDomain.AssemblyResolve event for the AppDomain in which I am creating an instance of Exposed
Having IExposedIface.dll both in Consumer's bin directory as well as aside each Exposed.dll.
Now consider that I build Exposed against this IExposedIface:
public interface IExposedIface
{
string SaySomething();
}
and I build Consumer against this IExposedIface:
public interface IExposedIface
{
string SaySomething();
string SaySomethingDifferent();
}
In the first case, the exception
Exception: Method 'SaySomethingDifferent' in type 'Exposed.Exposed' from
assembly 'Exposed, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
is thrown in the moment I call appDomain.CreateInstanceAndUnwrap(...) to create an instance of Exposed in the freshly created AppDomain.
That looks reasonable to me.
But in the second case, appDomain.CreateInstanceAndUnwrap(...) goes through just fine and I can without problems call 'SaySomething()' method on the retrieved object. An exception
The method 'SaySomethingDifferent' was not found on the interface/type 'IExposedIface.IExposedIface, IExposedIface, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null'.
is only thrown when I actually call SaySomethingDifferent() in Consumer.
I was quite surprised that in this second case CLR had let me go so far...Could someone explain why this is possible?
Case #1 means that Exposed.dll is binding against the wrong version of IExposedIface.dll - the metadata loader is able to detect this when loading the assemblies because it finds an unimplemented interface method.
Case #2 (probably) means that you have the correct version of each IExposedIface.dll besides each Exposed.dll so each assembly can load within its own AppDomain. However AppDomain A has a different interface than AppDomain B which is only a problem when the call actually crosses the AppDomain border.
I'd suggest not trying those binary compatibility games and rather do proper versioning (ie. create a new interface with the new methods, inheriting from the old interface, so the new version of IExposedIface.dll is really backwards compatible). Anything else is really hard to debug because you can accidently end up loading both versions of IExposedIface.dll if they are reachable for windows, and then you have two versions of a Type in the AppDomain causing no end of trouble ;)
Related
I'm trying to find a way to update an Asp.net web api (.Net framework 4.5) at runtime (without recycling the main appdomain) by adding new ApiController (downloaded by another service).
I tried to use Mef and was able to load the new ApiController in the current appdomain, but I got stuck when trying to update an existing plugin (the assembly is already added to the appdomain, so I can't add the new one).
So I decided to load the plugin containing the ApiController in a separate appdomain and use MarshalByRefObject to load it from the main appdomain but it turns out that ApiController cannot be serialized.
Do you know how I could serialize it?
Do you know an alternative?
Edit:
I managed to load different versions of an assembly (in the same appdomain) if the assembly is signed, but it doesn't match my requirements.
I haven't used MEF (because it is as easy to implement its functionality from scratch, in contradiction to MAF), but this way I have some experience with bare AppDomains.
It is hard to tell much without seeing your code, but from what you wrote, it seems to me that you are confusing some things.
As you probably know and you already pointed out too, you can't actually update an already loaded assembly. Loading another version of it (having a different signature) means that you have two different assemblies loaded. The types within them will have different strong names. You could actually handle this if you want. The only way to unload an assembly is to unload the appdomain that contains it.
My problem is with this sentence:
... load the plugin containing the ApiController in a separate appdomain
and use MarshalByRefObject to load it from the main appdomain
Type (class) definition+code and instance data are two different things. Loading an assembly into an appdomain means you are loading type definition and code. Serialization comes into view when you want to transfer instance data across appdomain borders. You can't load type definition and code from an other appdomain as you wrote (actually you could but I doubt you need to). To be able to transfer instance data both sides need to have knowledge about the type definition of the instance being transferred. The serialization and the transferred in this case is managed by the .net remoting runtime.
You have two choices: either move all the instance data and have it serialized all the time or you choose MarshalByObjRef way as you said you did. Let's stay with this. To be able to work with an instance in an other appdomain, you will need to instantiate the type in the other appdomain using the activator (you can't use the new operator in this case), and get a reference to it which will be a proxy based on the type you know (that can be an interface or a base class too, not only the exact type). Reflection is somewhat limited in such a situation, even less is prepared asp.net to figure out methods of a remote object - but you could help it with proper interfaces.
So, let's imagine you have created an instance of the controller in the other appdomain, and you have a remoting reference on it assignable to an interface type that defines all methods you need to expose to asp.net. Now serialization will come into view when are trying to access the members of the controller class. Each method parameter and method return type needs to be serializable. But not the class itself, as it is a MashalByObjRefdescendant and will not be mashalled as an instance. And MashalByObjRef has nothing to do with how you are loading the assembly into the appdomain.
But wait! Both MarshalByObjRef and ApiController are abstract classes. How do you want to derive your actual controller class from both? You can't. Thus I don't think you can directly use apicontrollers from an other appdomain directly.
I could imagine two things:
1) Stay with loading the new signed version into the same assembly and make customize the routing mechanism to direct requests to the latest version (might not be still valid, but could be still a good starting point: https://www.strathweb.com/2013/08/customizing-controller-discovery-in-asp-net-web-api/).
Of course, on restart, you should load only the latest one if you don't need to have multiple versions in parallel.
2) Make a slightly complex infrastructure:
define an interface for the controller logic
create the apicontroller versionless and logicless, but capable of creating and unloading appdomains, loading assemblies into them, keep reference to the instances implementing the interface from above created in them, and directing the requests to those
be aware that you won't be able to pass some things (like controller context) to the logic in the other appdomain, you will have to extract what you need or recreate on the other side
this way you can have the logic MarshalByObjRef descendant in the "remote" appdomain and your controller ApiController descendant in the main appdomain.
I would create an interim abstract class extending ApiController with the capability of handling the above separation on its own. The rest of the application wouldn't be aware of this.
be aware of the lifetime services involved in remoting, which you can handle either by using a sponsor or overriding some methods of MarshalByObjRef.
Neither is simple approach, you will be facing some further challenges...
I have a .NET dll wrapper around a mixed (Managed/Unmanaged) type. If some crucial dll's are missing from the hard drive or their location is not entered into the path, then the type will not load resulting in a TypeLoadException("Could not load file or assembly or one of its dependencies"). This hampers xcopy deployability of any utility that consumes this class library.
I would prefer to fix this problem without instructing all clients to change client code. I hope to achieve this by running code before the type is loaded by the CLR. I have included its dependencies in a zip file which is included with the distribution.
Clients call into a static factory method.
var MyMixedTypeInstance = MyMixedType.Create();
However, since the factory method signature return type is MyMixedType, then MyMixedType is attempted loaded before any code inside the create method is executed. I have considered make the return type an interface to avoid this. But if the interface at some deals with (eg returns) a concrete mixed type, then AFAICT I'm back to square one.
I attempted stuff like using a static constructor, but it seems (as maybe all of you others know) that types exposed by the public API are immediately loaded. However, I do not know much about how the loading of types unfold, so I may be missing something obvious. I know that you can help the CLR resolve assemblies, but I do not know if this relates to a type.
Naturally, it would be possible to create a complete separate "MakeSureNeededBinariesAreInPlaceAndInPath" kind of method and demand/force all clients to invoke it prior to calling the Create() method, but I would like to avoid it if I can.
Is there for instance any attribute I can decorate the type with to intercept the type loading of the class?
I am searching for the correct assembly setup and abstraction to get this issue solved:
I got an app using an assembly to access an external system. Now this assembly is not available on every computer as it must be installed before my application (it comes with the complete external system setup).
When I now start my app on a computer where this external system is not installed I get: "FileNotFoundException" (I've got an Application.ThreadException handler)
I would like to only "lously" reference that assembly and be able to run without it (do offer configuration, advice, simulation).
I guess I have to implement a wrapper but I am not sure where to start. Will the wrapper reference the real assembly or use reflection to call it when available?
I think you have already kind of answered your question. The best way to go about it is by implementing a "wrapper" that can take objects from this assembly and utilize the concrete objects of this assembly, then implement a stub that will take over when this assembly is not found.
This wrapper could be a proxy object if you implement the Proxy Pattern and it should be able to have some intelligent logic to switch to a different concrete object(s) when the above-mentioned assembly is not found. Since you are using c#.net it should be as simple as checking whether the assembly exists in a specific location without trapping the check logic within a try/catch block.
Edit
By the way, the wrapper cannot reference the assembly because the assembly is not included in your project...you are manually loading the assembly which is different. Then, in order to instantiate objects of this assembly and call methods of these objects you need to use reflection.
You could handle the AppDomain.AssemblyResolve event. It's called when an assembly cannot be found, and it gives your code the opportunity to track down the specified assembly and return it back to the app domain.
I'm writing a clipboard/image uploading tool in C#, where a keycombo would e.g make a screenshot, then send the image to a plugin and then the plugin handles the uploading and returning of an URL.
Now, those plugins need to save settings, and those settings need to be editable inside my application, so I thought I'd use a data saving class I wrote a while ago, and pass a reference of an initialized instance of that class to the plugins.
The idea was to put that entire class into an include, which a plugin writer could just include in their project to handle the data, however despite both the plugin and main application using the exact same code, I get an error about the versions being different, namely this:
{"[A]CedInc.Persistence.XMLPersistenceDictionary.savenode cannot be
cast to [B]CedInc.Persistence.XMLPersistenceDictionary.savenode. Type
A originates from 'CloudBoard, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null' in the context 'Default' at location
'C:\git\CloudBoard\CloudBoard\bin\Debug\CloudBoard.exe'. Type B
originates from 'CloudBoard FTP upload plugin, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null' in the context 'LoadFrom' at
location
'C:\git\CloudBoard\CloudBoard\bin\Debug\plugins\CloudBoard FTP
upload plugin.dll'."}
My question is: Is it possible to pass an instance of a custom class to a plugin without having to put it in an assembly to be loaded by the plugin itself? and if so, how? Any idea/suggestion is welcome! I'm kinda stuck here.
While I would recommend a shared assembly version, you could alos supply your plugin authors a template code file with pre-defined class and method names. And then you could use reflection to search the loaded assembly to locate the classes and methods matching your predefined signatures. Once you find the methods, it is easy to Invoke them.
You expect plugins not to reference any of your code at all?
In this case you may be able to find some interface in Framework assemblies that works to be passed to plugin instead of class. Maybe IDictionary or IDictionary<string,string> would work. This way plugin will not need to reference any of your assemblies for serialization and you will be able to pass you custom implementation. As side benefit for plugin authors will be much more convinient to unit-test their plugins (with concrete class form a random library it is much harder).
I have an application which is throwing an AmbiguousMatchException when I call AppDomain.CreateInstanceAndUnwrap to instantiate a type in another AppDomain. This is happening on a customer's computer which I don't have direct access to. I think the problem is that there are two copies of the same assembly loaded. Is there any way to figure out if this is the case and where the two assemblies are being loaded from? Will enabling the fusion log provide any additional information?
The fusion log might help, but another option might be to hook the AssemblyLoad event:
AppDomain.CurrentDomain.AssemblyLoad += (s, a) =>
{
Console.WriteLine(a.LoadedAssembly.FullName);
Console.WriteLine(a.LoadedAssembly.CodeBase);
};
There are two main causes of this error:
coincidental naming - i.e. Foo.dll and Bar.dll both have a Some.Namespace.Type type
different versions (mainly GAC) referenced by different components - i.e. your DAL loads v2 of some dll, and your UI/utility code loads v4 of the same dll
Of course, another option is that your AppDomains have infected each-other (it is very (too?) easy to accidentally suck a reference over an AppDomain boundary by exposing it on the API of the MarshalByRef object).