Accessing Resources Using LoadFrom Locks Assembly


Mark Tucker asks this question:


<quote>


I have the need to place a resources-only assembly in a well-known directory location that is not the GAC or under the ApplicationBase.  I then use Assembly.LoadFrom to load the resource, assembly.GetManifestResourceStream() to get the ResourceSet stream, and new ResourceSet(stream) to create a ResourceSet from the stream.  This fulfills our requirement, but the assembly is now locked and can’t be overwritten. 


When I do something similar using LoadWithPartialName and the assembly is in the ApplicationBase, then a Shadow copy of the assembly is made and I am able to update the assembly without tearing down the AppDomain.  Is this a deficiency with LoadFrom?  How can I get the same functionality as in ApplicationBase?  This is an ASP.NET 1.0 application.  I have read that you shouldn’t use .resources files as they will be locked.  That is why I am using assemblies.


Your help is greatly appreciated.


</quote>


Only assemblies in AppDomainSetup.ShadowCopyDirectories will be shadow copied. And they will be shadow copied in both Assembly.Load() and Assembly.LoadFrom(). ASP.NET set the shadow copy directories to bin directory under the ApplicationBase. That is why your Assembly.LoadFrom() did not shadow copy the assembly since your assembly is outside of the ApplicationBase.


One possible workaround can be to add the interesting directory to shadow copy directories. The following code snippet should do it:


AppDomain.CurrentAppdomain.SetShadowCopyPath(AppDomain.CurrentAppDomain.SetupInformation.ShadowCopyDirectories+”;”+<the interesting directory>);


Alternatively, you can manually copy the assembly to a temporary location then call Assembly.LoadFrom() on the temporary location. That way you won’t lock the original assembly.


If you only have one assembly to worry about, you may like the alternative more.


According to our developer on resource, the reason we tell people to use satellite assemblies in ASP.NET instead of .resources files,  is so that they get shadow copied automatically. Unfortunately your resource assembly is not under the default shadow copy directories.


Edit: Suggest an alternative. It is possible that we will mark AppDomain.SetShadowCopyPath as deprecated in the future.

Comments (5)

  1. Mark Tucker says:

    Thank you for your help.

    If I go the route of copying the assembly to a temp location, how do I go about getting the new assembly when it is updated?

    Won’t the old assembly be cached in the LoadFrom context and so I will always get the old one?

    Would I need to incorporate a FileWatcher on the assembly file in the well-known location to know that it changed and then copy the new assembly over the file in the temp location?

    Won’t the assembly in the temp directory then be locked?

  2. You can definitely come up with solutions.

    1. You don’t have to get notification when the assembly is updated. Old appdomains still uses the old assemblies, and new appdomain will use the new assembly. Once the assembly is loaded it can’t be unloaded anyway, so unless you destroy the old appdomain, it won’t make a difference.

    2. For the temp file locking issue, you can use a unique temp file each time. You don’t have to use the same location each time, do you?

    There may have other issues. But I am sure you can find solutions.

  3. Mark Tucker says:

    If the assembly is not strong named, then the version info will not be used as part of the identity. Since all other parts of the identity are the same, then the LoadFrom call will use the cached old assembly. Is my understanding correct? Then how do I get the new assembly to be recognized? When updating the assembly, how is it determined that a new appdomain should be created?

  4. what I am saying is you can’t force the old appdomain to use new bits. This is not possible.

    Asp.Net monitors files in bin directory, if there is any file change, it will shutdown the old appdomain, and restart it. This is how Asp.Net uses the updated bits.

    Since your assembly is outside of ASP.Net, and not monitored by ASP.Net, if you want similar behavior, you have to do it yourself.

  5. Justin Rogers gave a better suggestion on microsoft.public.dotnet.framework.clr

    "Justin Rogers" <Justin@games4dotnet.com> wrote in message news:e6zwGbHOEHA.892@TK2MSFTNGP09.phx.gbl…

    > LoadFrom short circuits the default probing logic and loads an assembly directly

    > from a specified location. The fact that a

    > shadow copy is not made is by design. Generally LoadFrom is most effective when

    > used in the AssemblyResolve event as

    > a last ditch effort to locate an assembly that can’t be found through probing.

    >

    > What you want is the byte[] overload for the Load method. This allows you to

    > create a dynamic assembly in memory from

    > which you can load your resources without locking the files on disk (except

    > during the load operation where you are reading

    > the file into your byte[]). The byte[] overload is quite commonly used in

    > plug-in infrastructures to prevent locking, with the

    > underlying issue of the dynamically generated assembly not being easily resolved

    > if you lose your reference to it. Multiple calls

    > to Load with the same byte[] will each load new versions of the assembly into

    > memory (aka no resolving path… If you

    > constantly LoadFrom the same path, you’ll only get the 1 assembly, not many of

    > them.)

    >

    >

    > —

    > Justin Rogers

    > DigiTec Web Consultants, LLC.

    > Blog: http://weblogs.asp.net/justin_rogers