ApplicationContext.User is lost in Main Window

ApplicationContext.User is lost in Main Window

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


BBM posted on Monday, September 15, 2008

Hi,

I'm hoping someone can educate me as to what's going on here.  I'm using Csla 3.03 and WPF.

I have a very simple menu form that is my main application form.  Here's the class declaration, along with the constructor, the InitInvoke handler for the DataPortal, and the handler for one of the menu items.

Partial Public Class MyMenu

   Public Sub New()

   ' This call is required by the Windows Form Designer.

      InitializeComponent()

     AddHandler Csla.DataPortal.DataPortalInitInvoke, AddressOf InitInvokeHandler

   ' Detect whether a valid Security principal has been set. If not, call one of the Logins

   If Application.principal Is Nothing Then

      Dim frm As AutoLogin = New AutoLogin()

   End If

End Sub

 

Public Sub InitInvokeHandler(ByVal obj As Object)

' This routine makes sure that the current thread has a copy of the logged on Principal for security purposes

   If Not ReferenceEquals(Application.principal, Csla.ApplicationContext.User) Then

      Csla.ApplicationContext.User = Application.principal

   End If

End Sub

Private Sub ItemBtn_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)

   Cursor = Cursors.Wait

   Csla.ApplicationContext.User = Application.principal  'Will not work without this

   Dim selection As EditFrm = New EditFrm

   Cursor = Cursors.Arrow

   Me.Visibility = System.Windows.Visibility.Hidden

   selection.ShowDialog()

   Me.Visibility = System.Windows.Visibility.Visible

End Sub

Doesn't get any simpler than this.  The AutoLogin form is a special version of my regular Login that automatically Logs in my test user.  It sets both ApplicationContext.User and my global user "cache" as described in the Csla 3.0 e-book.  I've located the global static cache variable in the WPF 'Application' object. AutoLogin also closes itself.

If I single step through the constructor above, after the call to instantiate AutoLogin in fact all the way through the End Sub for the Constructor,  Application.principal (the User cache), and ApplicationContext.User are set correctly and agree with each other.

But when I click one of the items on the displayed menu form and its handler is run, ApplicationContext.User has reverted to the "generic" user.  Application.principal still contains the correct User object.  Hence the call to reset ApplicationContext.User to Application.principal in the button handler.  But why do I need to do this???

In trying to de-bug this, I set a breakpoint in the ApplicationContext.User Property.Set accessor in Csla.  It breaks when AutoLogin initially sets the value, and breaks again when explicitly set in the handler.  But nothing else seems to be changing it.  I guess it could be that the constructor for the main form  is running in a different thread from the button handler?   

One final observation / puzzlement.  I'm running a "local" remote Dataportal, which means that I think I'm using IIS on my local computer to host the Server side Dataportal.  But each time the ApplicationContext.User property runs, HttpContext.User is Nothing, and ApplicationContext.User gets set to the Thread.Current value.  Is this correct?

Thanks for your help.

BBM

RockfordLhotka replied on Monday, September 15, 2008

The local data portal means the "server side" components run in the client process. There literally is no server, no cross-process communication, nothing. So there's no HttpContext, because your code is running in the WPF client process.

The principal disappearing in WPF is a known issue (Msft calls it a feature) of WPF, where they clear the principal. Total pain in the @$$. I discuss a workaround in the Using CSLA .NET 3.0 ebook. It isn't perfect, but it is the best I've come up with thus far...

BBM replied on Monday, September 15, 2008

Hi Rocky.

Thanks for your response.

I'm still puzzled about the "server side" when using a remote DataPortal that is hosted on the development machine.  So even though the DataPortal is hosted by IIS and I have to "attach" to the ASP worker process to de-bug it -  it's running in the client process??

Also, if I understand you correctly about the "lost" principal, the workaround in the 3.0 e-book just fixes DataPortal access.  There are other places in WPF (like the one I stumbled over) where the principal can be lost?

Thanks again.

BBM

RockfordLhotka replied on Monday, September 15, 2008

Read chapters 2 and 4 to understand the data portal.

 

The data portal runs in 1 of 2 modes.

 

In local mode THERE IS NO SERVER. At all. It is not hosted in IIS. There is no server. The “server-side” components run in the client process.

 

In remote mode there is a server. It might be hosted in IIS, a custom Windows service, a remote EXE, Windows Activation Service or anywhere else you can host WCF, remoting, asmx or COM+. For testing we have a “server” that simply runs on a different thread in the same Windows process – but it is truly separate from the client, which is what counts.

 

 

You are right, my ebook technique only fixes the data portal issue with the principal. There are other places in WPF you can get into trouble. I don’t have a good answer, and I think Microsoft really owes us a good answer, because their design in WPF seems really flawed to me.

 

Rocky

RockfordLhotka replied on Tuesday, September 16, 2008

I'm working on Chapter 19 now - the WPF UI chapter. So I took the time to run through this problem (the issue with a principal) in some more detail this morning.

It is a hard problem. The solutions offered by Microsoft are not good ones - specifically they recommend setting the default principal for the AppDomain as the app starts up. Which is silly, because the user has to be able to log in and out while the app is running...

Not only that, but when I tried their technique, it seemed to conflict with WCF - causing the client-side WCF proxy to lock up. I don't know why - hard to debug a total freeze...

So, after spending hours on that, I tried a different approach. This approach appears to work quite well - for CSLA code. I don't know that it will totally fix .NET code (like CAS), but it absolutely makes CSLA code work, along with any other code that drives off Csla.ApplicationContext.User.

Take the User region from this code
http://www.lhotka.net/cslacvs/viewvc.cgi/trunk/cslacs/Csla/ApplicationContext.cs?view=markup

and replace your User region in ApplicationContext.

This should allow you to skip the data portal InitInvoke event and everything - it just works. Notice how the User property now detects whether the code is running in WPF and manages the principal itself in that case. By using a static field, I'm making the value available across all threads.

If you can try this and confirm that my change is correct, that'd be very helpful - thanks!

BBM replied on Thursday, September 18, 2008

Hi Rocky,

No problem.  I'll let you know how it works.

I really like WPF and want to use it, but sometimes I think they should have named it WTF.

BBM

BBM replied on Thursday, September 18, 2008

Hi Rocky,

I compared the User region from your link to what I'm using.

I'm using the VB version of Csla, so I had to translate the C# to VB.  The only difference that I see from what I'm using (3.03) is that the User Property is declared static, but since ApplicationContext in the VB version is a module and not a class, User is already treated as static.  The editor/compiler won't even allow me to make the User property shared.

 So I'm confused as to what is changed?

BBM

RockfordLhotka replied on Thursday, September 18, 2008

      Private _principal As IPrincipal

 

      ''' <summary>

      ''' Get or set the current <see cref="IPrincipal" />

      ''' object representing the user's identity.

      ''' </summary>

      ''' <remarks>

      ''' This is discussed in Chapter 5. When running

      ''' under IIS the HttpContext.Current.User value

      ''' is used, otherwise the current Thread.CurrentPrincipal

      ''' value is used.

      ''' </remarks>

      Public Property User() As IPrincipal

        Get

            Dim current As IPrincipal

            If HttpContext.Current IsNot Nothing Then

              current = HttpContext.Current.User

            ElseIf System.Windows.Application.Current IsNot Nothing Then

              If _principal Is Nothing Then

                  If ApplicationContext.AuthenticationType <> "Windows" Then

                    _principal = New Csla.Security.UnauthenticatedPrincipal()

                  Else

                    _principal = New WindowsPrincipal(WindowsIdentity.GetCurrent())

                  End If

              End If

              current = _principal

            Else

              current = Thread.CurrentPrincipal

            End If

            Return current

        End Get

        Set(ByVal value As IPrincipal)

            If HttpContext.Current IsNot Nothing Then

              HttpContext.Current.User = value

            ElseIf System.Windows.Application.Current IsNot Nothing Then

              _principal = value

            End If

            Thread.CurrentPrincipal = value

        End Set

      End Property

 

BBM replied on Thursday, September 18, 2008

Hi Rocky,

This is working fine.  I've got to run now, but I'll test it some more tonight and let you know if I run into anything.

BBM

RockfordLhotka replied on Thursday, September 18, 2008

Good, I appreciate it!

 

I’ve been using the changed code for a couple days now, and it does seem to make everything much nicer.

 

Rocky

ajj3085 replied on Friday, September 26, 2008

I'm hitting this bug..

In what version is the above fix included?

RockfordLhotka replied on Friday, September 26, 2008

Certainly in 3.6, and I think I made the change to 3.5.2 as well (check the change log).

 

Of course “fix” is somewhat of a strong word. I fixed ApplicationContext.User, NOT all of .NET. So CSLA will work, and your code will work (if you use ApplicationContext.User), but things like CAS or other pure .NET features won’t work, because I’m not able to change how .NET manages the principal for itself.

 

Rocky

 

 

From: ajj3085 [mailto:cslanet@lhotka.net]
Sent: Friday, September 26, 2008 2:39 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: RE: RE: ApplicationContext.User is lost in Main Window

 

I'm hitting this bug..

In what version is the above fix included?


ajj3085 replied on Monday, September 29, 2008

Well, whatever you call it, it's all I need at the moment.. since the problem is the data portal is getting a GenericPrincipal, not my principal.  So it will be fine for my needs.  Smile [:)]

Copyright (c) Marimer LLC