I have some C# code (let's call it "script") I am compiling at runtime. It uses an interface in my main program that I use to access its functions. Once compiling is done I have CompilerResults.CompiledAssembly in which case I can CreateInstance(Type).
Once I am done using the script I would like to unload completely. From what I understand, I can only do this if I create a separate app domain:
Loading DLLs into a separate AppDomain
I had some questions specific to my implementation:
If I have multiple scripts to compile and want to unload them independently, do I have to create separate app domains for each?
What app domain names should I use? Would GUIDs be a good idea? Are there any names I should avoid that may conflict?
If the assembly is in a separate app domain, will it have any issues accessing the interface in the main program? I am currently doing ReferencedAssemblies.Add(typeof(Interface).Assembly.Location) before I compile.
Can I use CompilerParameters GenerateInMemory=true, or do I have to save it somewhere?
Answers in order:
Yes, if you want to unload them independently you'll need separate app domains.
Use whatever. It may help you debugging if you can identify it back to script, but that would be more true for the executing Thread.
Not so long as you set the domain setup's base path to your own.
AppDomainSetup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
No you don't need to save it and it doesn't sound like it would benefit you.
Related
I have a web application that refers to a DLL. This DLL has certain config values, which are normally part of the web.config.
There is a requirement to invoke this DLL under different configurations, from the web application. The DLL is third-party and cannot be changed.
Have tried the below and failed.
Have different copies of DLL (named a.dll, b.dll) and load it by reflection. The problem here is that it’ll still look for the web.config and not the a.dll.config. Also, since same Types are referenced in the main program as well as the reflected assembly, it goes crazy.
Change the config on the fly using AppDomain.CurrentDomain.SetData("WEB_CONFIG_FILE", #"Config\Path") and switch it back after the call. The problem here is that after the first time, it doesn’t load the config section again even if I switch.
Use ConfigurationManager.RefreshSection(#"configuration\mysection") to force a refresh. This doesn’t seem to work and people say this call is buggy in .NET
I've seen some recommendation to update the web.config, but this may not be a good choice for me because the switching of values would happen fairly frequently
Is there anything else I can do?
Host the DLL in a separate process and communicate using COM (or .Net remoting or a web service or similar).
I.e. create a host process a.exe using C# (say) which exposes classes as COM objects, the classes in turn calling the DLL methods/classes. Register as COM objects.
Then create b.exe the same (but with different CLSIDs).
You can now have different configuration files for a.exe and b.exe (in different folders), yet both can use the DLL services.
You could also do something similar by having two internal web apps and using SOAP or something to talk to them.
But the bottom line is if the DLL works on web.config, you have to put at least one of them into a separate process in a separate folder.
I am trying to make a C++/MFC application (so called "Update Application") that can extract AssemblyVersion information from other applications written in C#/.NET 3.5.
So I can update my C# program according to the version information I can get.
I am pretty sure this is possible, but I don't know which way would be the best way.
I would like to know some techniques or keywords that I can search on the web.
A direct explanation would be appreciated, too.
Here's roughly how we do something similar in a native C++ app.
Compile with /clr. You can do this project-wide or just on selected C++ files, but as far as I remember there were complications doing it selectively and so we just did it project-wide. Also #include <vcclr.h> wherever appropriate.
You'll need to learn about app domains. The main thing here is that once you've loaded an assembly into a domain, you can't unload the assembly except by unloading the entire domain. Since you want to load an assembly, query its version, and then let it go, you'll probably want to create a temporary domain and load into this.
In our system we have a managed C++ class called ModelLoader to load object models, query their version info, and discard them - just like what you want to do. This class is the pivotal element in our managed/unmanaged marshaling.
The code in the ModelLoader has to execute in the temporary domain, because we want it to load the target assemblies there and then unload the domain However, the main app is already running in the main domain and so it needs to be able to marshal method calls across to the ModelLoader in the temp domain. So ModelLoader inherits System::MarshalByRefObject, which allows the .NET runtime to do all the marshaling magic.
So the basic steps are something like this:
Load the assembly that contains the code for ModelLoader. In our system this is built into our main unmanaged .EXE and so we just use Reflection::Assembly::GetExecutingAssembly() to get a handle to it. If your equivalent of ModelLoader is in a separate assembly then you'll have to load it somehow. But since you probably won't need to unload this assembly you can load it into the main domain.
Create a temporary domain.
Create an instance of your ModelLoader class (obviously it will have a different name in your system) within the temporary domain.
Marshal a handle to that new instance back to your main domain.
Use the marshaled handle from within your main domain to execute code in the temp domain.
Unload the temporary domain.
So, in code:
AppDomain ^domain = AppDomain::CreateDomain(L"temp domain");
Assembly ^assembly = Assembly::GetExecutingAssembly();
ObjectHandle ^handle = domain->CreateInstanceFrom(
assembly->Location,L"ModeLoader");
Object ^o = handle->Unwrap();
ModelLoader ^loader = dynamic_cast<ModelLoader^>(o);
loader->GetAssemblyVersion(...);
AppDomain::Unload(domain);
To save you some head-scratching, the namespaces involved are:
System::AppDomain
System::Reflection::Assembly
System::Runtime::Remoting::ObjectHandle
System::Object
Within your ModelLoader, you'll want to load the target assembly and query its version info. Compared to all the other stuff, this is straightforward:
void ModelLoader::GetAssemblyVersion(const wchar_t *filename, AssemblyName ^name)
{
Assembly ^assembly = Assembly::Load(gcnew String(filename));
name = assembly->GetName();
}
(I made this function up just now, so it might not be quite right.)
Another thing to watch out for is assembly resolution. This is how the assembly loader resolves assembly names to DLL files. This is quite a large field in its own right, so I won't talk any more about it right now. (And in any case I'm no expert.) To get started, just make sure that all the assemblies you want to load are in your main app directory and I think you'll be more or less OK. Then when you have the basic loading working, you can worry about more sophisticated resolution.
I have a MEF-based application that can be customized with plugins. This application has several imported parts, and I want to remove some of them at runtime (to be able to delete the .dll that contains them) when a user decides to get rid of that plugin.
CompositionBatch would do what I need, but it needs ComposablePart instances as input parameters for RemovePart() method, and I only have plain objects that implement ISomething interface, or ComposablePartDefinition instances in the AggregateCatalog. So the my question is:
How can I find the ComposablePart
instance that represents the imported
object that I want to get rid of?
Or
alternatively: how do I get the list
of ComposablePart objects that belong
to a certain .dll?
I would use something like follows:
var parts = Container.Catalog.Parts
.Where(p => iDontNeed(p))
.Select(p => howDoIConvertComposablePartDefinition2ComposablePart(p));
var batch = new CompositionBatch();
parts.ToList().ForEach(part => batch.RemovePart(part));
Thank you
how do I get the list of ComposablePart objects that belong to a certain .dll?
To remove the parts from a particular assembly, you could just remove that AssemblyCatalog by calling AggregateCatalog.Catalogs.Remove. You'll have to design your parts to allow for Recomposition though.
However, this will not help you to delete the plugin assembly. The assembly will still be loaded and you cannot change or delete a .NET assembly while it is loaded. The only way to unload an assembly without stopping the process is by unloading the AppDomain in which it loaded. But if you introduce a separate AppDomain for the plugins then you'll basically have to communicate with those plugins via remoting, etcetera.
It's probably much simpler, safer and effective to just stop the application, delete the plugin assembly, and restart.
edit: actually, there is a way to delete the plugin dll file without stopping the process or unloading the entire appdomain: you can enable shadow copying for the app-domain to instruct .NET to make a copy before loading the assembly. The copy will remain loaded, but at least you can delete or replace the original file.
Could MAF help you here? I am not an expert but I understand that with MAF the addins remain in their own process and can be unloaded at run time. I'd guess that this would not give the ideal performance as you are communicating cross process but if that is not a major issue it might be worth taking a look at.
I have a web application that dynamically loads assemblies based on database configuration entries to perform certain actions (dynamic plugin style architecture).
The calls to the objects are in a Factory Pattern implementation, and the object is cached (in a static dictionary<> within the Factory) as the calls can be made many thousands of times in a minute.
The calls to this factory are made from both the main web application and a number of webservices, some in different assemblies/projects.
When I need to update one of these DLLs, I have to recycle IIS to get the DLL released.
As this has an impact on another application on the server, I wanted to know if there was a way I could release the DLL without restarting IIS?
There's absolutely no way to unload a loaded assembly other than killing the AppDomain which is basically what you are doing when you restart IIS.
You can try restarting the application pool not the whole IIS server. Maybe that will do the trick for you
If you have an assembly that you need to load and unload you will have to jump through a few hoops.
the types being loaded must derive from MarshalByRefObject
the types being loaded must derive from an interface that will be used to call them
you must build a remoting based 'loader' to isolate the loaded assembly in a new appdomain, which can be unloaded.
see http://www.west-wind.com/presentations/dynamicCode/DynamicCode.htm for a good introduction. It is a bit dated and deals with dynamically generated code but given your rep I would assume that you can extract the relevant information.
What are some guidelines and best practices for when to create new application domains within an application?
Also, what are some common uses and examples of how multiple application domains are used whithin an application?
The most common scenario I've seen is to be able to provide extensibility with a different security model than the main program.
Loading a plugin in a separate AppDomain allows two things:
You can implement a more restricted security model
You can prevent the plugin from tearing down your application if it's buggy
Another nice use of AppDomains are to load and inspect an assembly. Doing this in a separate AppDomain allows you to glean information (or run code) from a separate assembly, then unload the entire assembly from your process's space. If you load the assembly directly, there is no way to unload it. This is also useful if you want to be able to, at runtime, "upgrade" a type to a new version (ie: load a remote assembly, and reload it later).
It is recommended to create new domain when you need to host 3-rd party components within your application that are unreliable or you do not trust them (like plug-ins) or you want to be able to unload them.
A typical example is for plugin-/addin-like cases. Not only does it allow you to unload the DLL if required, it also gives you better security control over what the plugin is allowed to do.
Also, if you create temporary assemblies (code generation) which you want to unload again, this is a good way to do it. (LCG only allows implementing single methods, if you want to implement a complete class you need to emit to a "real" assembly).