PolicyHelper assembly loading and ReflectionTypeLoadExceptions

Jan 9, 2014 at 5:31 AM
Hi Ryan,

In PolicyHelper.LoadAndCachePolicyHandlers I would like to recommend changing AppDomain.CurrentDomain.GetAssemblies() to BuildManager.GetReferencedAssemblies().Cast<Assembly>() (ref: System.Web.Compilation).

This is for two reasons:

First, AppDomain.CurrentDomain.GetAssemblies() is non-deterministic (depends on statements about to be executed) whereas BuildManager.GetReferencedAssemblies() is deterministic (depends on referenced assemblies and web configuration).

This can mean assemblies are missing from AppDomain.CurrentDomain.GetAssemblies() depending on which code is executed (or about to be executed).

Second, AppDomain.CurrentDomain.GetAssemblies() can include assemblies that are not referenced by the application (e.g. attached debugger, process host extensions).

This can mean additional unexpected assemblies make their way into AppDomain.CurrentDomain.GetAssemblies().

This preference of BuildManager.GetReferencedAssemblies() seems to be supported by StackOverflow answers (http://stackoverflow.com/a/2479400/364) and use by the actual MVC team (http://stackoverflow.com/a/3704939/364).

Without this change I encounter a bug where authorisation throws a ReflectionTypeLoadException depending on which page I visit first (i.e. depending on which assemblies make their way into the AppDomain for the first page load).

Specifically in my situation (though not relevant to the fix or MVC Authorization project) if I visit a page using WCF components there are (unused) behaviorExtensions loaded into the CurrentDomain.GetAssemblies() call that raise an exception when the PolicyHelper attempts to access them. BuildManager.GetReferencedAssemblies() does not encounter this issue as the behaviorExtensions are not referenced by the solution.

Currently my workaround is to have a copied LoadAndCachePolicyHandlers method in my own solution with the necessary change from:
var policyHandlerTypes = AppDomain.CurrentDomain.GetAssemblies()...
to:
var policyHandlerTypes = BuildManager.GetReferencedAssemblies().Cast<Assembly>...
and to use reflection in Application_Start to update the PolicyHelper's private _policyHandlerTypeListCache field to use my method instead.
typeof (MvcAuthorization.Policy.AuthorizationPolicy).Assembly
                .GetType("MvcAuthorization.Policy.PolicyHelper")
                .GetField("_policyHandlerTypeListCache", BindingFlags.Static | BindingFlags.NonPublic)
                .SetValue(null,
                    new Lazy<List<Tuple<Type, PolicyMetadataAttribute>>>(() => MyFixedLoadAndCachePolicyHandlers()));
However this is not exactly ideal or future-proof so it would be greatly appreciated if this change could make its way into the core.

Cheers,
Matt
Coordinator
Jun 14, 2014 at 2:07 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.