CSLA and SQL Cache Dependency.

CSLA and SQL Cache Dependency.

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


geordiepaul posted on Monday, July 24, 2006

I have been trying to implement some server side SQL Cache Dependencies withing my business objects. Basically within my DataPortal_Fetch I create the dependency and add an event handler to expire the cached data when the data changes within the DB.

The problem I'm having is that the SQLDependency.Start() methods fails when the Csla.ApplicationContext sets the Thread.CurrentPrincipal to the Csla version of the principal/identity. It reports that it can't find my assambly! The application web based so I have changed the User property of the ApplicationContext class to the following....

public static IPrincipal User
{
   get
   {
      
if (HttpContext.Current == null)
         
return Thread.CurrentPrincipal;
      
else
         return HttpContext.Current.User;
   
}
   set
   {
      
if (HttpContext.Current != null)
         HttpContext.Current.User = value;
      else // <--- NEW
         Thread.CurrentPrincipal = value;
   }
}

This works fine but I am a little concerned as to the impact this will have and I can't find any documention covering this area of SQL Cache Depenencies.

Just wondered if anyone had experinced the same problems??

Thanks

 

RockfordLhotka replied on Monday, July 24, 2006

Are you trying to run your code within IIS or Cassini? CSLA custom security won't work in Cassini - I've blogged about this and mention it several places in the book as well.

Your change is dangerous, because it means that the thread's principal isn't being set and may not match the httpcontext principal. While all CSLA code uses ApplicationContext, and yours probably does too, any code not based on CSLA may be doing the wrong thing and using the thread's principal when in ASP.NET (certainly any .NET 1.x code would do that) - and thus would be using the wrong principal.

david.wendelken replied on Monday, July 24, 2006

FYI, Cassini is the name for the webserver built into Visual Studio.

geordiepaul replied on Tuesday, July 25, 2006

No, I'm using IIS. This is the error I get when I remove the "else" from the above code.

 

Unable to find assembly 'AssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Runtime.Serialization.SerializationException: Unable to find assembly 'Communicator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

Source Error:

Line 35:             if (!m_DependancyStarted)
Line 36:             {
Line 37:                 SqlDependency.Start(ConnectionString);
Line 38:                 m_DependancyStarted = true;
Line 39:             }

Source File: C:\Documents\Visual Studio 2005\projects\....    Line: 37

geordiepaul replied on Tuesday, July 25, 2006

One thing I should add is that this only fails in ASP.NET applications. If I call the same method in a Console Application then it works as expected even with my "else" added, since...

HttpContext.Current == null

Thread.CurrentPrincipal gets set anyway.

RockfordLhotka replied on Tuesday, July 25, 2006

Sure, that would work fine in a console app.
 
But the problem is, that in an ASP.NET app you wouldn't set the thread principal. Some code (lots actually) rely on the thread principal only, because (in theory) that's the right thing to do. Thank's to Scott Hanselman I discovered that in .NET 2.0 they changed some things, so there are some edge cases where the thread principal isn't safe to use in ASP.NET (which really sucks), and so this is why ApplicationContext does what it does.
 
If you ONLY use ApplicationContext, and you KNOW that you are only using code that uses it (or an equivilent) then you could get away with your change. But you really can't, because that means you can't use any 3rd party controls/components and even the .NET framework itself is not sure to work (all the security demand attributes use the thread principal...)
 
My guess is that SqlDependency is switching threads, and that it (or ASP.NET) is using dynamic assembly loading. There's a known issue deep, deep in the .NET runtime that causes problems with finding dynamically loaded assemblies during deserialization. So deep that they can't/won't fix it - nor have they officially agreed that it is a bug, but it is very clearly not desired behavior...
 
The possible solution is to handle the appdomain event for failure to resolve the assembly, and to handle it yourself.
 
I do this in Chapter 4 for the implementation of the EnterpriseServices data portal host, because this issue exists for any dynamically loaded assemblies in COM+. You can find the code in Chatper 4 called "Serialization Workaround". In a web setting you'd want to call the hookup once in global.asax, as the initial hook should just run once per appdomain. The rest is automatic.
 
Whether this fixes your problem or not I don't know - you'll have to try and see.
 
Rocky


From: geordiepaul [mailto:cslanet@lhotka.net]
Sent: Tuesday, July 25, 2006 4:00 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] CSLA and SQL Cache Dependency.

One thing I should add is that this only fails in ASP.NET applications. If I call the same method in a Console Application then it works as expected even with my "else" added, since...

HttpContext.Current == null

Thread.CurrentPrincipal gets set anyway.




xal replied on Tuesday, July 25, 2006

Yes, that is very crazy!! I learned that the hard way. For me, the app was raising exceptions when I closed some forms that where using csla objects. The problem was that it wasn't finding csla's assembly.

I do this when the app starts:
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ResolveAssembly

And the function does this:
    Private Function ResolveAssembly(ByVal sender As Object, _
                ByVal e As ResolveEventArgs) As Assembly
        For Each ass As Assembly In AppDomain.CurrentDomain.GetAssemblies
            If e.Name.Contains(ass.FullName) Then
                Return ass
            End If
        Next
        Return Nothing
    End Function

(Yeah, I know I didn't pick the best name for the assembly variable, but ins spanish it doesn't mean anything) Smile [:)]


Anyway, the reason for this to happen is beyond me. It is a winforms app. I could never find the cause.

Andrés

geordiepaul replied on Wednesday, July 26, 2006

Hi, Thanks for you help and advice.

I have tried to implement the event handler with the Application_Start. The ResolveAssembly method never fires (even though the event is correctly connected through the application_start event) and the problem persists.

The code is global.asax looks like this....

void Application_Start(object sender, EventArgs e)
{
   
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(ResolveAssembly);
}

private System.Reflection.Assembly ResolveAssembly(Object sender, ResolveEventArgs e)
{
   
foreach (System.Reflection.Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
   {
   
   if (assembly.FullName == e.Name)
      
return ass;
   
}
   
return null;
}

Could the SqlDependency.Start() method be operating outside the ASP.NET AppDomain? It's not an area I'm particularly knowledgeable?

 

RockfordLhotka replied on Wednesday, July 26, 2006

Yes, I imagine it could. I haven't looked at this area at all, so I really don't know... If that is the case, you may be in a difficult position - but in that case it would be similar to issues faced when doing nunit tests.
 
The workaround for nunit tests is to make sure to do a Logout() before allowing the appdomain transition. So what you might try is wrapping your call to SqlDependency in a method. Have that method first store the value of Thread.CurrentPrincipal, then clear it (to GenericPrincipal or Nothing/null), then call SqlDepdendency, then restore Thread.CurrentPrincipal to its original value.
 
Rocky

Could the SqlDependency.Start() method be operating outside the ASP.NET AppDomain? It's not an area I'm particularly knowledgeable?

geordiepaul replied on Wednesday, July 26, 2006

That's a great idea and it worked too! Big Smile [:D]

I already had the call to start the dependancy in the method in a database class so it was a simple change and here's is the code for anyone who has a similar problem...

/// <summary>
///
Starts the SQL Cache dependancy if it not already started within the
///
current application
///
</summary>
public static void
StartSqlCacheDependancy()
{
   if
(!m_DependancyStarted)
   {
      System.Security.Principal.
IPrincipal
principal;
      principal = System.Threading.
Thread
.CurrentPrincipal;

      System.Threading.
Thread.CurrentPrincipal = null
;
      
      try
      {
         SqlDependency
.Start(ConnectionString);
         m_DependancyStarted =
true
;
      }
      catch (Exception
ex)
      {
         throw
(ex);
      }
      finally
      {
         System.Threading.
Thread
.CurrentPrincipal = principal;
      }
   }
}

RockfordLhotka:

The workaround for nunit tests is to make sure to do a Logout() before allowing the appdomain transition. So what you might try is wrapping your call to SqlDependency in a method. Have that method first store the value of Thread.CurrentPrincipal, then clear it (to GenericPrincipal or Nothing/null), then call SqlDepdendency, then restore Thread.CurrentPrincipal to its original value.
 
Rocky

Could the SqlDependency.Start() method be operating outside the ASP.NET AppDomain? It's not an area I'm particularly knowledgeable?

ljcorreia replied on Monday, February 14, 2011

Hi,

I know this post is quite old, but I had this problem for quite a while and never came across this solution, so the SqlCacheDependency I have for Sitemap was never getting noticated in SQL 2005 and therefore I never was able to solve this problem until I bang into this thread.

I just want to say thank you very much. I also solved my problem.

Kind regards,

Leo

Copyright (c) Marimer LLC