VSTO and CSLA 2.0 Error

VSTO and CSLA 2.0 Error

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


dfmartin posted on Tuesday, September 05, 2006

I am using CSLA in a VSTO Outlook add-in project.  I am getting the following error:

Exception of type 'System.Runtime.Serialization.SerializationException'. ... 'Unable to find assembly 'CSLA, Version=2.0.0.0,. . .. '

I narrowed it down to a call to set the user on the app context with :

ApplicationContext.User = (IPrincipal) myCustomPrincipal.

I do have a custom principal object inherited from BusinessPrincipalBase.

I also noticed that when under VSTO the Thread.CurrentPrincipal is always GenericPrincipal unless I set the current principal to null first.  Wondering if anyone else has used CSLA with VSTO and specifically the security.

Thanks,
David

 

RockfordLhotka replied on Tuesday, September 05, 2006

I haven't tried using CSLA under VSTO, but I can help with your first problem, as it is a common issue any time .NET is hosted within another process.

You can find details about the issue and solution by looking in Chapter 4, at the EnterpriseServices data portal channel - specifically the discussion about the "serialization workaround". Or if you are looking in the .NET 1.0 book, look at Appendix A, because NetRun has the same issue, and the same solution.

The gist of the problem is this: when .NET is hosted by another process, the type resolution process sometimes fails to find types that are already loaded in the AppDomain. This is because, behind the scenes, there are four lists of loaded types, and for some reason not all four lists are always checked. I believe this is a bug in .NET - but it is apparently so deep in the CLR that they don't dare fix it...

In any case, the serialization workaround code in the EnterpriseServices host class in Chapter 4 should solve your problem.

dfmartin replied on Tuesday, September 05, 2006

Thanks to both Rocky and Andy.  I have found the section in chapter 4 to which you refer.  However, I'm at a bit of a loss as to where to place this workaround.  Andy, where in your VSTO project did you end up placing this?

In the mean time I'll be experimenting with several places for the code.

Thanks much,
David

ajj3085 replied on Tuesday, September 05, 2006

David,

Sorry I wasn't clear.

Everytime Outlook raises an event to which my addin is listening, I simply get the current user's principal by settings Csla.ApplciationContext.User = new WindowsPrincipal( WindowsIdenitity.GetCurrent() );

This works for me since I'm tying directly to the WindowsPrincipal.  What kind of principal are you trying to use?

ajj3085 replied on Tuesday, September 05, 2006

David,

Yes, I've used VSTO to create an Outlook 2003 add-in as well.  You can do it. Smile [:)]

Rocky points out the solution to your problem; one addition note as far as the principal goes is that it seems to be 'forgotten' the next time your add-in is called.  In my case, I listen for ItemAdd on the Inbox and Sent Items box.  Each time the event is fired, the principal seems to be reset to Generic.  I got around this by always getting the current WindowsPrincipal (since I'm using Windows auth), but you may be able to by using SetPrincipalPolicy.

One more word of caution, as this bit me.. if you're responding to Outlook events (which I assume you are), make sure to keep static references to the items which are rasing the events to which you're listening.   If you don't, the GC comes along and cleans everything up and your events start disappearing.  In my case, holding the MailBox wasn't enough, I had to hold a reference to the Items collection.

HTH
Andy

dfmartin replied on Tuesday, September 05, 2006

Andy:  I've tried placing code similar to that found in the solution mentioned by Rocky in several places in my VSTO project and I'm not getting any solution. 

First, I tried placing it in the ThisApplication_Startup - I made a call to SerializationWorkaround and from there it is identical to Rocky's solution with the exception that my code is not static. 

Second, I tried the above but starting with the static constructor of ThisApplication and using static methods just like Rocky.

Third, I tried both 1 & 2 above but this time in my business object.

Regardless of where I place the code I still get the same error.  Any further advice?  Also, I placed breaks within the AssemblyResolve event handler and it is apparently never getting called.

Thanks much,
David

 

ajj3085 replied on Tuesday, September 05, 2006

David,

I would THINK that you put the code in ThisApplication_Startup.  Can we see some more code?  

dfmartin replied on Tuesday, September 05, 2006

Andy:  Here is my code from ThisApplication.cs

private void ThisApplication_Startup(object sender, System.EventArgs e)
{
 
SerializationWorkaround();
 
this.InitializeReleaseTokenAddIn();
}

private void SerializationWorkaround()
{
 
AppDomain currentDomain = AppDomain.CurrentDomain;
 
currentDomain.AssemblyResolve += new ResolveEventHandler
     (
currentDomain_AssemblyResolve);
}

Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
 
Assembly[] list = AppDomain.CurrentDomain.GetAssemblies();
 
foreach ( Assembly asm in list )
   
if ( asm.FullName == args.Name )
     
return asm;

 
return Assembly.Load(args.Name);
}

Some more info:  When I step through my code I can execute the line where I set the ApplicationContext.User object to my custom principal without error.  The error occurs on a line where I have the following:

frmMyCustomForm.ShowDialog()

Now, in the constructor of that form I am creating a new business object.  In the constructor of the business object I am setting the ApplicationContext.User object.  I am guessing this is where the problem is because I can comment out the simple set of Thread.CurrentPrincipal (from with in the Application.Context.User settor and all works fine (except I don't have a way of keeping the ApplicationContext.User in sync with the Thread.CurrentPrincipal.) 

Maybe I don't need them to be in sync except that all of this has worked perfectly for a while now when doing ASP.Net or simple WinForms.  This problem has only appeared now that I am doing some VSTO stuff.

Thanks again,
David

 

ajj3085 replied on Wednesday, September 06, 2006

David,

It sounds like the first time you set AppContext.User to your custom principal everything is fine; when you first create the principal, can you store it in a static variable somewhere so that you always have it?  Then whenever your code is fired from Outlook set AppContext.User to the stored principal?

I honestly didn't have any issues with finding the assembly... do you have Csla and your business assemblies set to Copy Local?  If not, try enabling Copy Local for those references.

dfmartin replied on Wednesday, September 06, 2006

I'm not sure what was going on.  I may have explained it wrong but I never got past the first time I set AppContext.User.  In fact, I bypassed that and set Thread.CurrentPrincipal directly and that caused the exact same problem.

As for what dataportal I'm using, currently it would be the no network option as everything is currently local.  Also, everything about data access is working.  I am able to retrieve objects from the DB and insert/update objects to the DB no problem.  In fact, that's what confused me most - to be told that it could not find the CSLA assembly eventhough it had just gotten through using that assembly.

Anyway, for now I am trying a custom property on the AppContext.CustomUser.  Inside of this I am using the pattern from AppContext.GetClientContext (and it's sister SetClientContext).  I'm placing my custom principal into the data slot.  When I retrieve the user, if its null, I do a CustomPrincipal.Login() and that performs all of my custom login stuff.

So far it appears to be working.   Problem is that I am not yet used to using the named data slots on a thread so I hope I not doing anything terribly wrong by placing my custom principal there.

Thanks,
David

 

ajj3085 replied on Wednesday, September 06, 2006

David,

I'd be careful about using the thread storage; I'm not sure if you'll be guarenteed that your code will always execute on the same thread.

It almost sounds like a version mismatch problem; as you said, if your principal is based off of a Csla class, its kind of hard to understand why the assembly isn't being found.

Could you post more code from the add-in, like where you create your principal?  Did you double check to make sure that your principal is Serializable?

dfmartin replied on Wednesday, September 06, 2006

Here's my code as I remember it (I've changed it since to what I wrote above):

In the business object's New factory method:

public static Application NewApplication()
{
   Application app = new Application();
   app._id = Guid.NewGuid();
   ... more property sets

   WillisIdentity i = ApplicationContext.User.Identity as WillisIdentity
   if ( i == null )
   {
      WillisPrincipal.Login();
      i = (WillisIdentity)ApplicationContext.User.Identity;
   }

   app.userid = i.ADGuid.ToString();
   return app;
}

The WillisPrincipal.Login (again, as I remember it)

public static bool Login()
{
   AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
   if ( Thread.CurrentPrincipal is GenericPrincipal)
      Thread.CurrentPrincipal = null;

   WillisIdentity ident = WillisIdentity.GetIdentity(Thread.CurrentPrincipal.Identity.Name);

   WillisPrincipal prin = new WillisPrincipal(ident);

   ApplicationContext.User = prin;

   return ident.IsAuthenticated;

}

The WillisIdentity.GetIdentity takes the username and does an AD lookup.  If it finds the user in AD it populates certain fields on the identity object.  Fields like, display name, ADGUID, email address, first and last name.

Not sure if all that helps.

Thanks for all of your help on this,
David

 

RockfordLhotka replied on Wednesday, September 06, 2006

I haven't been following this thread in detail, so I could be off base here. BUT...
 
It is possible you are running into the same issue people run into when using custom security and nunit tests - AppDomain switching.
 
Odds are good that VSTO is running your code in an AppDomain, different from the app itself, so they can unload your code later. This is also what nunit does.
 
What can happen in this case, is that your code sets up the principal and all is good within your AppDomain. But eventually the thread exits your AppDomain and returns to the main AppDomain. When that happens, the principal object is serialized, moved to the main AppDomain and deserialized. Obviously your assemblies aren't available in the main AppDomain, so you get an assembly not found exception during deserialization.
 
The solution to this in nunit testing is to make sure that you set the principal to a GenericPrincipal before the call returns to the main AppDomain. With nunit tests this is pretty easy - because each test method is a clearly defined entry/exit point. With VSTO hopefully it is also relatively clear?
 
Rocky

It almost sounds like a version mismatch problem; as you said, if your principal is based off of a Csla class, its kind of hard to understand why the assembly isn't being found.

ajj3085 replied on Wednesday, September 06, 2006

This sounds reasonable to me.  Unfortunatly, I don't think VSTO offers anything so clear.  The work around could be to check that the assembly resolve event is handled each time your code runs.. perhaps not nice, but it should work.


RockfordLhotka replied on Wednesday, September 06, 2006

Unfortunately that won't help if my suspicion is correct. The reason being that you can't handle the event in the main AppDomain, only in the sub-AppDomain where your code runs. The exception would be happening in the main AppDomain when it tries to deserialized the principal there...
 
Rocky

This sounds reasonable to me.  Unfortunatly, I don't think VSTO offers anything so clear.  The work around could be to check that the assembly resolve event is handled each time your code runs.. perhaps not nice, but it should work.

borota replied on Thursday, March 06, 2008

I was having exactly the same problem with VSTO 2.0. Resetting the Principal to be a GenericPrincipal works. In the form's Close event I'd reset the Principal to be GenericPrincipal, and right after the ShowDialog call, set it back to CSLA principal.

I discovered a better solution though. Just drop a BackgroundWorker control on your form while in design mode. You don't necessarily need to do anything with it, just having it, makes the serialization exception go away.

I don't know why this takes care of the issue, if you do, please share.

andrew123 replied on Monday, June 16, 2014

I've just encountered this problem in a Word VSTO using .NET 4.5.1 and CSLA 4.5.501

The serialization workaround made no difference to me, but luckily I have come across this post by MikeGoatly: 

http://forums.lhotka.net/forums/p/11545/55404.aspx#55404

This solved my issue - I had to force the ConfigurationManager to reinitialize BEFORE I set my Principal on the thread.  Not entirely sure why but it works!  If I don't call the ConfigurationManger to force it, I get a Serialization Exception regarding my custom Principal class.

JonM replied on Tuesday, September 05, 2006

What type of dataportal are you using?  I've played with CSLA.net 1.53 and the VSTO 3.0 beta (for office 2007) and did not have to use any type of workarounds.  I just added all the CSLA references to my project and created a app.config file with the dataportal config in it.   In fact, it was so easy it was ridiculous.  In outlook 2007 we have support for custom task panes.  In visual studio design-mode you simply build a custom user control and insert it into the custom pane.  I build a little demo app that let us search contacts in a custom pane with like 25 lines of code.

Copyright (c) Marimer LLC