Load and Unload Assembly in own AppDomain

1

I do web. service that load assembly file by input parameter. Then in assembly will try to find a specific type (inherited from specific interface), create an instance and returns the result of the method.

I need to make the call to the assembly was again released.

From the input parameters of the method I find the path to the the assembly in web.config. and try to load it.

This is the code that works:

[WebMethod]
    public String[] GetData(String confKey)
    {
        var assemblyPath = ConfigurationManager.AppSettings[confKey];
        var assembly = Assembly.LoadFrom(assemblyPath);

        List<String> retVals = new List<String>();

        foreach (var t in assembly.GetTypes())
        {
            if (t.ImplementsInterface(typeof(IMyServiceProvider)))
            {
                IMyServiceProvider objectInstance = Activator.CreateInstance(t) as IMyServiceProvider;
                retVals.Add(objectInstance.GetData());
            }
        }

        return retVals.ToArray();
    }

But this way I can delete the loaded assembly or replace it because the file is "locked".

So I tried to go a different way and load the assembly into own AppDomain like this:

[WebMethod]
    public String[] GetData(String confKey)
    {
        var assemblyPath = ConfigurationManager.AppSettings[confKey];

        var tmp = String.Concat("AppDomain", Guid.NewGuid().ToString("N"));
        AppDomain dom = AppDomain.CreateDomain(tmp, null, AppDomain.CurrentDomain.SetupInformation);
        AssemblyName assemblyName = new AssemblyName();
        assemblyName.CodeBase = assemblyPath;
        Assembly assembly = dom.Load(assemblyPath);

        List<String> retVals = new List<String>();

        foreach (var t in assembly.GetTypes())
        {
            if (t.ImplementsInterface(typeof(IMyServiceProvider)))
            {
                IMyServiceProvider objectInstance = Activator.CreateInstance(t) as IMyServiceProvider;
                retVals.Add(objectInstance.GetData());
            }
        }

        AppDomain.Unload(dom);
        return retVals.ToArray();
    }

But this solution is thrown Exception:

Could not load file or assembly 'NameOfMyAssembly' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)- at System.Reflection.AssemblyName.nInit(RuntimeAssembly& assembly, Boolean forIntrospection, Boolean raiseResolveEvent) at System.Reflection.RuntimeAssembly.CreateAssemblyName(String assemblyString, Boolean forIntrospection, RuntimeAssembly& assemblyFromResolveEvent) at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection) at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) at System.AppDomain.Load(String assemblyString) at System.AppDomain.Load(String assemblyString)

Why the first solution assembly loads without problem, and the second throws an error ? Thanks

c#
web-services
asked on Stack Overflow Oct 26, 2013 by Davecz

1 Answer

2

dom.Load(string) method tries to load the assembly by its full name which consists of assembly name, version, culture info and public key. CLR search the assemby in GAC and application's directory. Also you could set additional searching paths with .config file (app.config or ) and probing element. In your case as I understand assemblyPath is a path to the assembly you whant to load and it isn't what need to pass as parameter. I used next code to load assemblies in created domain:

var domain = AppDomain.CreateDomain("Plugin domain"); // Domain for plugins
domain.Load(typeof(IPlugin).Assembly.FullName); // Load assembly containing plugin interface to domain 
// ...

var asm = Assembly.LoadFrom(pluginFile);
foreach (var exportedType in asm.GetExportedTypes())
{
    if (!typeof (IPlugin).IsAssignableFrom(exportedType))
        continue; // Check if exportedType implement IPlugin interface
    domain.Load(asm.FullName); // If so load this dll into domain
    // ...
}

As all plugins lies in "\Plugins" directory relatively application directory I also added .config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <probing privatePath="Plugins"/>
    </assemblyBinding>
  </runtime>
</configuration>

Pay attantion that if you whant to use dom.Load(AssemblyName) with specified only CodeBase property you must set it in Uri format (i.e. "file:///D:/Dir/...") and I think these assemblies have to be strongly named if they are not in the current application’s folder.

Also as I see you are usig Activator.CreateInstance() to create objects. In this case although you load assembly in created domain you create objects in current domain and all code of this objects be executed in current domain too. To create and execute code in a separate domain you should use appDomain.CreateInstanceAndUnwrap() method (link)

answered on Stack Overflow Oct 26, 2013 by Deffiss • edited Oct 26, 2013 by Deffiss

User contributions licensed under CC BY-SA 3.0