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.
Related
I'm beating my head against the wall trying to find a container that will accomplish this.
What I'd like to do is have a AS.NET website running and not unload / recycle the AppDomain when I deploy a new or updated business rule contained in an assembly. This implies that the folder is outside of the bin folder, and preferably above it, not under it (although I can live with that). The closet I've come to this so far is using Autofac and MEF, but it seems like there's no way to unload a previously loaded assembly.
Anybody have any resources they can point me to?
Thank you,
Stephen
No, there is not. Because it is not possible to unload an assembly, only a total appdomain. THis is a .NET runtime limitation - and as every .NET IOC container has to live in the .NET runtime it can not bypass it.
If your imports are big enough isolating them in separate appdomains may be a good and viable idea.
You can not unload assemblies in .Net without unload whole AppDomain. So the only way to allow it is to load new assemblies into new AppDomains.
While it may be possible to build such IoC container that will marshal all requests to new AppDomains for many interfaces such code would put very significant restrictions on methods/objects exposed by the interfaces. Also many .Net objects can't cross AppDomain boundary (xml, UI/controls, HTML context, database related classes).
It is significantly easier to allow ASP.Net to deal with reloading of the AppDomain.
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 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.
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).
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.