none
Resolving and loading referenced assemblies outside application root directory RRS feed

  • Question

  • Hi,

    We have the following deployment structure:

    1. Client assemblies - C:\MyProduct\Client\

    2. Server assemblies - C:\MyProduct\Server\

    On our test environment, we create an extra folder which has all the automated unit test dlls i.e. C:\MyProduct\TestDlls\

    We need to run MsTest on the unit test dlls (C:\MyProduct\TestDlls\). But, the dependent dlls (for which the unit test cases are written) are outside i.e. C:\MyProduct\Client\ and/or C:\MyProduct\Server\

    If I understand correctly, the probing technique in .Net would fail as the dependent assemblies are not under the subdirectories.

    Kindly let me know how can I load the assemblies at the runtime so that I can run the unit test cases. 

    Kindly note that I don't have the liberty to change the directory structure :(

    Kindly provide a code snippet. 

    Thanks in advance.

    Regards,

    Asim.

    • Moved by Mr. Wharty Monday, December 23, 2013 12:40 AM Not a Training and Certification question
    Sunday, December 22, 2013 8:36 AM

Answers

  • Correct, probing only works with subdirectories.  The only real solution that I can think of is to handle the appdomain's Resolve event.  When an assembly fails to load it raises the event.  Handle the event and search for the assembly in whatever path you want.  Provided you return back the assembly then the CLR is happy.  This would allow you to search for the assembly using whatever paths you want.  Note however that this can complicate things because: a) dependency versioning tends to get complicated if assemblies are being loaded from different paths and b) generally you have to use LoadFrom which has quirks of its own.

    Michael Taylor
    http://msmvps.com/blogs/p3net

     
    • Marked as answer by Asim Patnaik Tuesday, December 24, 2013 5:31 AM
    Monday, December 23, 2013 3:29 PM
    Moderator

All replies

  • Hi.

    Assuming you are running MSTest in the \TestDlls folder, you will need to add one or more application configuration file(s) to the \TestDlls folder.

    The application configuration file will contain additional paths for .NET's probing process to look for .NET assemblies.

    The application configuration file contains case-sensitive and space-sensitive content. It must be exact (no typos!) or else it won't use the file.

    Typically, the configuration file is named the same as the EXE that needs to load it with a .config on the end. For example, WinApp.exe would load WinApp.exe.config.

    DLLs can also use configuration files, starting with .NET 2.0. For example, if a DLL was called TestDll.dll, you would need to name it TestDll.dll.config.

    Your config file(s) might look something like this:

    <configuration>
       <runtime>
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
             <probing privatePath="..\Client;..\Server"/>
          </assemblyBinding>
       </runtime>
    </configuration>

    You can see how well probing and loading is working (called .NET fusion) by turning on logging in fuslogvw.exe. You start this from the Developer Command Prompt.

    Here are some important points:

    • It stops probing in additional folders as soon as it finds the DLL.
    • It always checks the GAC first before probing, if the DLL is signed.
    • You cannot attach to and debug a DLL in the GAC.

    Good luck!


    Best wishes, Davin Mickelson


    Sunday, December 22, 2013 10:21 PM
  • Thanks for the response.

    But if I am not wrong, we use privatePath only for subdirectories of the application's base directory as per the link http://msdn.microsoft.com/en-us/library/823z9h8w(v=vs.110).aspx

    Kindly help.

    Regards,

    Asim.

    Monday, December 23, 2013 2:04 PM
  • Correct, probing only works with subdirectories.  The only real solution that I can think of is to handle the appdomain's Resolve event.  When an assembly fails to load it raises the event.  Handle the event and search for the assembly in whatever path you want.  Provided you return back the assembly then the CLR is happy.  This would allow you to search for the assembly using whatever paths you want.  Note however that this can complicate things because: a) dependency versioning tends to get complicated if assemblies are being loaded from different paths and b) generally you have to use LoadFrom which has quirks of its own.

    Michael Taylor
    http://msmvps.com/blogs/p3net

     
    • Marked as answer by Asim Patnaik Tuesday, December 24, 2013 5:31 AM
    Monday, December 23, 2013 3:29 PM
    Moderator
  • Thanks for the response. I did come across the Resolve event. I will give it a shot. The very fact that this is for running the unit tests, I can afford to take some blips :)

    Regards,

    Asim.

    Tuesday, December 24, 2013 5:31 AM
  • I created a base class which is inherited by every unit test class. Below is the code:

    public class UnitTestExecutionBase
    {
    public UnitTestExecutionBase()
    {
    // Register the AssemblyResolve event to solve assembly resolution failures
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    }

    /// <summary>
    /// This event is fired when the MSTest is unable to resolve the dependent assembly
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    /// <returns></returns>
    public Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
    // Search for the assembly and return the same
    ...
    ...
    ...
    return Assembly.LoadFile(assemblyPath);
    }
    }

    In the above class, I log the error in case the assembly search fails.

    I am able to locate and load all the dependent assemblies.

    But now my test cases are failing with the below error message:

    Message - System.IO.FileLoadException: Assembly is still being loaded. (Exception from HRESULT: 0x80131016)
    StackTrace - at MyProduct.MyClass.MyTestMethod()

    Please let me know if I am doing something wrong.

    Regards,

    Asim.

    Thursday, January 2, 2014 2:05 PM
  • Not heard of that one before.  Unit tests run in parallel on multi core machines.  It sounds like one of the unit tests is in the process of resolving the assembly when another unit test tries to use the assembly.

    But I think the problem is with your hook.  You should only hook up to the event once.  It shouldn't really be causing a problem but it seems suspect.  Instead of hooking it up for each test instance try using TestInitialize instead and see if the issue goes away.

    Michael Taylor
    http://msmvps.com/blogs/p3net

    Thursday, January 2, 2014 6:21 PM
    Moderator
  • Thanks for your response.

    As rightly pointed out by you, I think there was a clash when the same assembly was being loaded by multiple threads and hence the error was being thrown. It will be great if someone can explain the technicality in detail.

    So I added a method to register the AssemblyResolve event to every test case class and marked it with ClassInitialize attribute (as I needed to register the AssemblyResolve event only once) and that seems to have taken care of the issue.

    Thanks a lot.

    Regards,

    Asim.

    Friday, January 3, 2014 8:44 AM