AppDomain, UserPrincipal, Serialization Problems (SL5, .NET 4.5, IIS Express)

AppDomain, UserPrincipal, Serialization Problems (SL5, .NET 4.5, IIS Express)

Old forum URL: forums.lhotka.net/forums/t/12065.aspx


bill.mybiz posted on Tuesday, July 09, 2013

Howdy All,

I have a pretty neat problem and I'd appreciate any and all help...You see, I am having a bit of a hiccup with trying to create and run code in dynamically-created AppDomains.


Here is my situation and troubleshooting so far:


I create an AppDomain object in the standard way that I've read up on. I've gotten this to work away from my CSLA projects and it works fine. Here is the code as it is this second:


public class ServiceManager

{

...

  [System.Security.SecurityCritical]
  private IAutonomousServiceContext CreateContext()
{

      AppDomainContext retContext = null;

      try
      {

        AppDomainSetup ads = new AppDomainSetup();
        ads.ApplicationBase =
            System.Environment.CurrentDirectory;
        ads.DisallowBindingRedirects = false;
        ads.DisallowCodeDownload = true;
        ads.ConfigurationFile =
            AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;

        var appDomainName = AutonomousResources.AppDomainNamePrefix +
                            DateTime.Now.ToLongTimeString() +
                            RandomPicker.Ton.PickLetters(Configuration.DefaultContextRandomSalt) +
                            AutonomousResources.AppDomainNameSuffix;
        AppDomain ad2 = AppDomain.CreateDomain(appDomainName, null, ads);
        //ad2.AssemblyResolve += ad2_AssemblyResolve;
        //ad2.Load("LearnLanguages.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
        var contextType = typeof(AppDomainContext);
        var assembly = contextType.Assembly.FullName;
        retContext =
          (AppDomainContext)ad2.CreateInstanceAndUnwrap(assembly,
                                                        typeof(AppDomainContext).FullName);

        return retContext;
      }
      catch (Exception ex)
      {
          Services.PublishMessageEvent(ex.Message, MessagePriority.VeryHigh, MessageType.Error);
        throw;
      }

  }

...

}


The original exception I got was:


"Type is not resolved for member 'LearnLanguages.Business.Security.UserPrincipal,LearnLanguages.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'."}    System.Exception {System.Runtime.Serialization.SerializationException}

After reading Rocky's reply for another question, I came to believe the problem stemmed from the assembly not being loaded into the AppDomain. Then when it goes to try to serialize my principal object (because of passing thread information to the newly created AppDomain), it can't serialize the thing because it can't find it in any of its assemblies. This is still what I believe to be the case, but I still am having the hiccup - I can't figure out how to get around it (yet).

I tried Rocky's workaround given in that article, but no dice. The workaround focuses on the AppDomain's resolve event handler. So, I implemented the handler, hooked it up - I tried this on the newly created child AppDomain, as well as even the main application's AppDomain (and both at the same time); however, each permutation left me a similar error:

Type is not resolved for member 'LearnLanguages.Autonomous.Core.ServiceManager,LearnLanguages.Autonomous, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'

This time, the error is not in my business assembly, but rather the calling assembly...the class it's balking on is the declaring class in the above code snippet is doing (trying to do) the creating of the child AppDomain! So it seems as soon as I try to do anything with the AppDomain object (which does indeed descend from MarshalByRefObject btw), it blows up on me.

I also tried to load assemblies explicitly, as shown in the commented line in the original code snippet, as well as follows...

//ad2.Load("LearnLanguages.Business, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

 

...but this also throws the exception about the UserPrincipal serialization problem.

So as best I can tell, the problem is that the second app domain can't be touched without initiating the problem of the current thread's UserPrincipal object. Do you think this is the case? If so, is there some hack way around this, or is there something else that I am missing?

Like I said, this is a pretty nifty problem, and I'm sure there's going to be an equally nifty solution...I'm just not thinking of it yet! Or, perhaps any of you could give me any suggestions for this?

 

RockfordLhotka replied on Monday, July 15, 2013

Unless you explicitly create a new thread for the new AppDomain, it will use the same thread as your calling code.

Any objects on the thread (the principal and anything in TLS) will be serialized and deserialized as the thread shifts across AppDomain boundaries. This is all done using .NET Remoting and uses the BinaryFormatter.

The BinaryFormatter needs to resolve the types of each object as it deserializes that object. This means that both AppDomains must have access to those same types.

If you can't ensure that your new AppDomain will have access to the principal/identity types on your thread, you have two real options.

First, you can set the principal on your thread to GenericPrincipal before shifting to the new AppDomain. This works because GenericPrincipal is part of .NET itself and so is always available.

Second, you can create a new thread for the new AppDomain. It will start out with a principal that is a .NET type (or null) and so will also work.

Of course neither of these solutions flows your principal to the new AppDomain. The only way for _that_ to happen is if your new AppDomain has access to the same type as the calling one.

Copyright (c) Marimer LLC