Quantcast
Channel: .Net
Viewing all articles
Browse latest Browse all 10

Load and unload assemblies

$
0
0

Last week, I had a problem that probably some .Net developer had faced. The problem consists in load and unload an assembly?.

Can I do that??? Well, I can’t do it in a simple way. One of the things that Assembly doesn’t support in unload, check this post to see why.

 

So, like I wrote, last week I was developing a new feature for a software factory and that feature consisted in get all references and do something else.

My recipe does something like this:

  1. Get project assembly
  2. Copy the assembly DLL to a location
  3. Get all assembly references
  4. Generate a file

 

Do number 1 is simple.

Do number 2 is also simple, but has a problem.

Do number 3 is also simple, but has a problem.

Do number 4 is simple.

 

I’m going to explain first problem number 3 then problem 2

The problem with number 3 it’s because I load a assembly and they I get referenced assemblies. To get referenced assemblies I just do:

Assembly assembly = Assembly.LoadFrom(@"c:\myAssembly.dll");AssemblyName[] references = assembly.GetReferencedAssemblies();

 

This piece of code has a problem, since I’m loading the assembly and they get referenced assemblies the file myAssembly.dll is now locked this means that the next time I try to  run number 2 I will get an exception and the file will not ne copied. And this is why I had problem  number 2.

Now, how can I load the assembly and then unload that assembly so the file won’t be locked???

 

I came across some solutions.

  1. One of the possible solutions is just copy the assembly to something unique like {GUID}.DLL and then load that {GUID}.DLL, and do everything I need to do, this way I won’t lock my file.
    1. Problem: I will have the assembly {GUID}.DLL loaded (in memory) every time, and can’t delete file {GUID}.DLL, because it’s locked. And finally I can’t unload unless I close Visual Studio.
  2. Why don’t I just load the assembly into an array of bytes and then load the assembly, like this:
    Assembly assembly = Assembly.Load(File.ReadAllBytes(@"c:\myAssembly.dll"));
    1. Problem: I can do this and my file will not be locked but that array of bytes will be in memory and my visual studio memory will grow every time I run that recipe. That memory will be released only when I close the Visual Studio.
  3. I can load the assembly in a new AppDomain and then unload the AppDomain.
    1. Problem: tThis was a strange problem and I don’t know why but I load the assembly in a new AppDomain and that assembly become part of the new AppDomain and the AppDomain.Current. This was strange. Don’t know if I was doing something wrong. Now since the Assembly is loaded in AppDomain.Current I wasn’t able to copy the file, because it was locked.
  4. The last solution I came across to load the assembly and unload it. This solution Consists in create a new AppDomain and then use method DoCallBack:

Here it is the source code:

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = ApplicationBasePath;AppDomain newDomainReferences = AppDomain.CreateDomain("AppDomain", null, setup);

CallBackAssemblyReferences call = new CallBackAssemblyReferences(newDomainReferences, filepath);
newDomainReferences.DoCallBack(new CrossAppDomainDelegate(call.LoadAssemblyReferences));AssemblyName[] assName = (AssemblyName[])newDomainReferences.GetData("AssemblyReferences");AppDomain.Unload(newDomainReferences);

 

This solution enables me to do a callback in a different domain. My class CallBackAssemblyReferences just does this:

[Serializable]internal class CallBackAssemblyReferences{#region Members Variablesprivate string assemblyFile;private AppDomain domain;#endregion

    #region Public Implementationpublic CallBackAssemblyReferences()
    {

    }
    public CallBackAssemblyReferences(AppDomain domain, string assemblyFile)
    {this.assemblyFile = assemblyFile;this.domain = domain;
    }public void LoadAssemblyReferences()
    {Assembly assembly = this.domain.Load(AssemblyName.GetAssemblyName(this.assemblyFile));

        domain.SetData("AssemblyReferences", assembly.GetReferencedAssemblies());
    }#endregion}

 

This class has a constructor that receives the new domain I’m working on and the path to the assembly I want to load.

public CallBackAssemblyReferences(AppDomain domain, string assemblyFile)

 

Then I just have my callback method WITH NO PARAMETERS.

public void LoadAssemblyReferences()

 

This is the method that will be called when I do

newDomainReferences.DoCallBack(new CrossAppDomainDelegate(call.LoadAssemblyReferences));

 

The callback method only loads the assembly in the new domain and then sets data in the new domain. These data are the assembly references.

When the DoCallBack returns I just had to do

AssemblyName[] assName = (AssemblyName[])newDomainReferences.GetData("AssemblyReferences");

To get the references I had saved.

 

Finally I just unload the AppDomain.

 

Now I can run my recipe every time I need without lock the file and without decrease visual studio performance.


Viewing all articles
Browse latest Browse all 10

Trending Articles