AppDomain.AppendPrivatePath is obsolete


In .Net framework 2.0, AppDomain.AppendPrivatePath is marked as obsolete.

The deprecation is explained in Gotdotnet.com’s break change web site

http://www.gotdotnet.com/team/changeinfo/Backwards1.1to2.0/default.aspx#00000102

<quote>

Title Cache load failures in order to ensure that different app domains do not have different dependency loading success/failure characteristics in domain neutral sharing scenarios.
Area System.Reflection
Affected APIs Deprecating Appdomain.AppendPrivatePath and Appdomain.ClearPrivatePath
Affected Scenarios This change would break code that is dependent on changing the private path of the app-domain after the first assembly has been loaded.
This is actually less common than one would think. ASP.Net though that they relied on this and later learned that they did not.
Description In V1 & Version 1.1, some pathways through the loader recorded a failure to load an assembly and would fail subsequent attempts to load that same assembly into the same AppDomain. However, most pathways through the loader would not cache this binding failure. This lack of caching enables certain scenarios, which some customers are doubtless taking advantage of. This includes a known xaml / JScript Avalon scenario which will break if we start consistently caching our binding failures. That particular scenario (VSV2.0 120644) executes code that contains an AssemblyRef to an assembly that doesn’t exist. Any load failures are trapped. Later, the missing assembly is generated via JScript and subsequent load attempts are expected to succeed.
In V2.0, we would like our new default behavior to break these scenarios. Naturally we would support an opt-in mechanism to force the loader back to the V1 & Version 1.1 behavior via a config file. We would break these scenarios by making the following changes:
– Cache all binding failures per assembly per AppDomain. Subsequent attempts would unconditionally continue to fail. Of course, transient errors like OutOfMemoryException are not considered binding failures and are subject to retry.
– Deprecate AppDomain.AppendPrivatePath and ClearPrivatePath. These APIs allow the application to dynamically change where we look for assemblies to satisfy binding requests. These APIs are subject to race conditions and cannot be reliably used, as I will explain.
Workaround There is no workaround

</quote>

If you have read my blog about Domain Neutral Assemblies and Caching Binding Failures, you will see that we are toward more on a deterministic binding behavior where we can make sane sharing decision without fearing that our assumption will be broken. Deprecating AppDomain.AppendPrivatePath is a step toward that goal.

The deprecation actually causes quite some confusion. People asks if there is a replacement.

There is no replacement in .Net framework 2.0. There will be no future replacement, as far as I can see.

The only reason I see why AppDomain.AppendPrivatePath can be any useful, is that your application will download some random bits, put them in a sub folder under your application, then use AppDomain.AppendPrivatePath to make them available to the application.

The approach has at least two problems.

First, it makes assembly binding undeterministic. Depending on the assembly binding happens before the download or after, you may see a failure sometimes, or a success the other times. This is exactly the reason why we deprecate AppDomain.AppendPrivatePath in the first place — WE WANT A DETERMINISTIC BINDING BEHAVIOR.

Second, on a typical Windows machine, applications are installed to C:\Program Files, which by default, only administrators can update. If you start download random bits into the application’s sub directory, it practically means your application can only run under administrator account. This is exactly the reason why Windows is so vulnerable, because EVERY APPLICATION REQUIRES ADMINISTRATOR ACCOUNT TO RUN. If you are doing this, please stop, and use your creativity to find alternatives.

Of course, I can’t really know all the cases how people use AppDomain.AppendPrivatePath. If you are using it, post your reason here. We can discuss the alternatives.

In any case, if you subscribe AssemblyResolve event, you can probably have your problem solved easily.

 

Comments (11)

  1. chris says:

    Great blog. This information is priceless. I was wondering if you could comment on the following, however. We’re wanting to place assemblies in a specific location outside of our ASP.NET application and I understand altering the path won’t help. However, when I place them in a web-accessible location, I can’t seem to add a <runtime> directive to machine.config to indicate the location of the dependent assembly. Your article here: http://blogs.msdn.com/junfeng/archive/2004/11/16/258081.aspx details XML that doesn’t seem to work as shown. Repeated here: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/gngrfCodeBase.asp this also seems not to work.

    Any advice on how you actually download or use an assembly from a location outside the ASP.NET app?

  2. Please read my article closely.

    <quote>

    1. For the runtime to use the <codeBase> setting in a machine configuration file or publisher policy file, the file must also redirect the assembly version (to a different assembly version, added by me).

    What this says is, when the <codeBase> hint is in publisher policy or in machine config, if there is no assembly version redirection, or assembly redirection does not result in a different assembly version, the codeBase hint will not be honored.

    </quote>

  3. David Levine says:

    I agree with the desire to achieve deterministics binding but I disagree with deprecating the use of AppendPrivatePath.

    I believe that forcing apps to use the AssemblyResolve event will lead to LESS deterministic binding. There will be more differences in how assemblies get resolved, not less. It would only make it more predictable for assemblies that are in the Load context – all others will be resolved differently for each application. If each app has to deal with it themselves, and if they get it wrong (and the liklihood of that will increase with the number of devs all dealing, perhaps incorrectly, with binding issues), the end user’s frustration with .NET will increase.

    There are other problems with deprecating this API. There are numerous valid use cases for an application to dynamically add assemblies into subdirectories below its appbase; this can occur anytime an app wants to load a new plugin that is located in its own folder. For reasons I’ve gone into in other threads it is not always feasible to load plugins in the LoadFrom context – conflicts can arise because some assemblies can get loaded twice, once in each context.

  4. chris says:

    The versioning stuff is quite a gotcha. It forces you to write an app to depend on a different version of an assembly if you want to use an external location for the "real" version, and its not apparent in your other discussions, like this:

    <quote>There are other solutions if you don’t want to install the assembly to GAC. You can put the assembly in a well known location. You then add a codebase hint for the assembly in machine.config. When you service the assembly, you upload the updated assembly in a new well known location and modify the codebase hint in machine.config.

    If you don’t want to modify machine.config, you still have options. You can add the codebase hint to the config file for all the applications that want to use the assembly. When you service the assembly, you update all the application config files to point the assembly to the new location. The advantage of this option is that the change only affects the applications you modified, instead of all the applications in the machine, as the first and the second option do. </quote>

    The fact that the assembly that is mentioned in the codebase hint MUST be a different version than what the application is looking for makes this a less than ideal solution if you are looking to just place assemblies in a common external location.

  5. David,

    Let me think more before I reply your comment. It is good to see you here.

    Chris,

    The paragraphs you quoted are for .Net framework 2.0 only.

    In .Net framework 2.0, codebase hint does not need a version redirect. This is mentioned in my original article.

    http://blogs.msdn.com/junfeng/archive/2004/11/16/258081.aspx

    <quote>

    Whidbey changes

    There is a slightly change of behavior for bullet (1) in Whidbey. We will honor the codebase hint in machine.config, even there is no assembly binding redirect, or assembly binding redirect results in the same assembly version. This change is directly influenced by customer request.

    </quote>

  6. David Levine says:

    Hi Junfeng,

    Thanks. It’s been a busy couple of months for me, I haven’t been up here much and now I am catching up with all the blogs I’ve neglected. I appreciate the time you spend on this.

    My concern is that if AppendPrivatePath is obsoleted then the only means of loading new assemblies/components into the Load context will be to force the host to restart, and this is not an acceptable solution. To solve our customer’s needs we need the loading and binding of assemblies to be dynamic, not static.

    I’ve a few ideas/wishes I’d like to propose that are related to assembly binding…

    Provide a means of identifying the context a particular assembly is loaded into. This would help troubleshoot problems related to multiple copies of the same assembly getting loaded into different contexts.

    Provide a means of promoting an assembly from a non-Load context to the Load context; this is essentially the function that AppendPrivatePath is performing. I think this would increase the determinism of binding because then it would take AssemblyResolve out of it entirely and leave all future binding decisions for that assembly to be handled by the Fusion layer, not the user’s application. An assembly would have to satisfy certain requirements to be promotable, such as being located below the appbase, for this request to succeed. A simple use case could be…

    AssemblyName an = Assembly.GetAssemblyName(localFilePath);

    Assembly a = Assembly.Load(an);

    if ( !a.IsInLoadContext && a.IsPromotable )

    a.Promote();

    Provide a binding preview mechanism. This would provide a means of determining if an assembly could get loaded, and if so, which one, if a Load request were made, without actually loading the assembly into the appdomain. This would fully evaluate all binding redirects, etc. I think I asked for this once before and there is some way to accomplish this, but I don’t recall the specifics.

    =====================

    The basic problem that I need to solve is how to make the loading and binding of assemblies predictable while running in a very dynamic environment. One scenario I am dealing with is that assemblies (components that perform a specific task) will need to be pushed out to various machines as a central scheduling authority determines work needs to be performed. The target machines are mission critical and cannot be shutdown. The target may or may not have these new bits already installed; the target cannot be shutdown or restarted in order for it to use the new assemblies (although it will need to synchonrize the loading of updates with existing components).

    One approach I can use is to load each into different appdomains – this would allow me to customize the binding for each appdomain separately before the component starts running, but this is not always feasible – some components need to run in the same appdomain as others. One reason why I cannot use separate appdomains in all cases is that we need to plug assemblies into a common display framework, but the CLR currently does not support separate appdomains participating in the same GUI.

    Thanks again for the time you spend up here with us.

    Dave

  7. David,

    Your discussion goes way beyond the scope of this article.

    I have an article about binding context, but I did not have time to finish yet. We should probably have a full discussion there.

    But it is sufficient to say that the direction is to use another appdomain for the add-in activation. Hopefully we will have all the problem sorted out in the next version. .Net framework 2.0 is too close to finish. There is really not much we can do now.

  8. By the way David,

    I can’t reach you on your email. Drop me an email if you did update your email.

    Thanks,

    Junfeng

  9. David Levine says:

    Hi Junfeng,

    I was recently forced to convert from Outlook to Lotus Notes; my email address changed but the IT group *promised* that all email sent to my old account would get forwarded to my new one – obviously this promise was like lots of others…

    Then to make my day complete my box was unbootable this morning…Clearcase burped or couldn’t digest something and refused to wake up – took most of the morning to get that fixed. It’s been that kind of day…

    I’ll send you an email later this weekend or 1st thing Monday with my new address. Thanks.

    Dave

  10. RealOne says:

    By the way David,