Application hang when using Remote WS portal, but OK when using local portal

Application hang when using Remote WS portal, but OK when using local portal

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


dag.oyvind posted on Tuesday, November 07, 2006

I meet some strange behaviour when I enable remote WS portal instead of using the default client/server portal. Using a combination of SCSF and the ultragrid from Infragistics everything works fine using the local dataportal. My code to fill a grid is as follows:

In the presenter

NOIS.WinTek.Business.EiendomListe eiendomListe = Business.EiendomListe.GetEiendomListe(eventArgs.Data);

View.LoadEiendomListe(eiendomListe);

the view simply sets the bindingsource as follows:

public void LoadEiendomListe(NOIS.WinTek.Business.EiendomListe eiendomListe)

{

this.bindingSourceEiendomListe.DataSource = eiendomListe;

}

So far, so good. This code runs perfectly when using the local dataportal. However when I use the remote portal the application hangs after setting the DataSource of the bindingSource. I have traced the code and the dataportal correctly returns and fills my business object (eiendomListe) with the correct count. I wonder if there is some sort of thread-locking problem here? I have traced deep into CSLA and found that the difference between local and remote is here:

if (proxy.IsServerRemote)

         ApplicationContext.SetGlobalContext(result.GlobalContext);

Further inside SetGlobalContext:

if (HttpContext.Current == null)

{

LocalDataStoreSlot slot = Thread.GetNamedDataSlot(_globalContextName);

Thread.SetData(slot, globalContext);

}

When I reach this code I have a returnObject with a correct record count of 22. But what does this code do? And why is it only run when using the remote portal? My business object is a

Csla.ReadOnlyListBase<EiendomListe, EiendomInfo>

Anyone had similar problems?

 

RockfordLhotka replied on Tuesday, November 07, 2006

The code you are seeing deals purely with ApplicationContext.GlobalContext, and really shouldn't have any effect on your actual business object. In fact, if you don't explicitly put something into GlobalContext, it is null all the way through the process and the code you are seeing is pretty much a non-operation.

The data portal itself is single-threaded. It doesn't create or use background threads. So if your code isn't creating/using background threads there shouldn't be a threading issue.

My guess is that you have some sort of circular parent reference within your object graph, or something along that line. Having circular references is fine, but the fields must be marked with NonSerialized and NotUndoable, and you must manually reset them on deserialization.

When using the local data portal, there is no deserialization, but the remote data portal must deserialize the data coming from the server.

To simplify your debugging, I recommend constructing a test that uses the local data portal, and then calls Clone() on your root object. Odds are that the clone will fail, because the Clone() method uses the same serialization process as the remote data portal - but it is easier to debug because it doesn't have all the complexity of the data portal and a remote network call.

dag.oyvind replied on Wednesday, November 08, 2006

Thanks Rocky, after further investigation I have narrowed the problem down to ReadOnlyBase, specifically the CanReadProperty in Csla.ReadOnlyBase. I believe this is a bug in CSLA (?)

In short, I found that if I run a remote WS portal, a null reference exception is thrown at line 253 in the Csla.ReadOnlyBase class.

if (AuthorizationRules.HasReadAllowedRoles(propertyName))

AuthorizationRules is null, so an exception is thrown

If I run the local dataportal, this will not happen and a call to CanReadProperty returns true. Even if I override AddAuthorizationRules and add a rule AuthorizationRules.AllowRead("Endret", "ROGateReadGroup"); I still get a null-exception when trying to bind the list to a grid using remote WS portal.

My workaround at the moment is to override CanReadProperty in my BO and always return true.

I have also discovered a an unrelated problem with my error-provider (it doesn't work using remote portal), but I'll look further into that before I post it to the forum.

Curelom replied on Monday, November 13, 2006

I'm also getting this same problem.  I'm not using WS.  I get this after I save.

RockfordLhotka replied on Monday, November 13, 2006

OK, let's narrow this down.

Are you VB or C#?

What version of CSLA are you using? 2.1?

dag.oyvind replied on Wednesday, November 15, 2006

This topic is directly related to http://forums.lhotka.net/forums/thread/8354.aspx in which we concluded that there must be something wrong with the latest CodeSmith template for ReadOnly objects.

They work fine for local dataportal but the code breaks (null reference exception) somewhere within the CanReadProperty of the ReadOnlyBase (CSLA 2.1, C#) object causing inconsistent behaviour between local and remote:

local portal: CanReadProperty does not throw an exception, hence Read = true
remote portal/bo.Clone(): CanReadProperty throws an exception, hence Read = false

To reproduce the error without having to set up a remote portal, one can simply clone an instance of a RO derived object /created using the latest C# CodeSmith template downloaded from codeplex), then try to access a property which has get { CanReadProperty(true); return _propName; } signature...

Dag.

JoeFallon1 replied on Wednesday, November 15, 2006

I took a quick look at this.

The C# template outputs code like this:

public string Acctcode
  {
   get
   {
    CanReadProperty("Acctcode", true);
    return _acctcode;
   }
  }

When I built my own VB template I used code like this:

 Public Overridable ReadOnly Property Acctcode() As String
      Get
        If CanReadProperty("Acctcode") Then
          Return mAcctcode
        Else
          Return ""
        End If
      End Get
    End Property

I think there is a big difference between the two.

Not sure how it affects things when remoting though.

Joe

RockfordLhotka replied on Wednesday, November 15, 2006

I remain unable to recreate this in CSLA (I'm not getting into debugging the templates). Here's my non-failing class:

  [Serializable]
  public class Test : Csla.ReadOnlyBase<Test>
  {
    private int _prop;
    public int Prop
    {
      get
      {
        CanReadProperty("Prop", true);
        return _prop;
      }
    }

    protected override object GetIdValue()
    {
      return _prop;
    }

    public Test()
    {
      _prop = 5;
    }
  }

And my non-failing test:

    private void Form1_Load(object sender, EventArgs e)
    {
      Test t = new Test();
      Test c = t.Clone();
      MessageBox.Show(c.Prop.ToString());
    }

If you can get a similarly clear test that fails, I would love to see it so I can track down the point of failure.

rasupit replied on Thursday, November 16, 2006

Rocky, I tested your sample on v2.1 and it fails on my NUnit test.  The null reference raised because AutorizationRules return null value.

I look at the ReadOnlyBase and notice the following code inside the constructor:
    /// <summary>
    /// Creates an instance of the object.
    /// </summary>
    protected ReadOnlyBase()
    {
      _authorizationRules = new Csla.Security.AuthorizationRules(this.GetType());
      Initialize();
      AddInstanceAuthorizationRules();
      if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
        AddAuthorizationRules();
    }


because _autorizationRules was marked NonSerialized, the variable would return null value after serialization.
I think if the code to initialize/load autorization rules be moved to Initialize() would solve this problem.

Ricky

RockfordLhotka replied on Thursday, November 16, 2006

Ahh, now I see...
 
Damn.
 
This bug was noted very early on and is fixed in 2.1.1.
 
So the real solution is for me to get the time to put together the 2.1.1 release...
 
Rocky
 


From: rasupit [mailto:cslanet@lhotka.net]
Sent: Thursday, November 16, 2006 6:17 AM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Application hang when using Remote WS portal, but OK when using local portal

Rocky, I tested your sample on v2.1 and it fails on my NUnit test.  The null reference raised because AutorizationRules return null value.

I look at the ReadOnlyBase and notice the following code inside the constructor:
    /// <summary>
    /// Creates an instance of the object.
    /// </summary>
    protected ReadOnlyBase()
    {
      _authorizationRules = new Csla.Security.AuthorizationRules(this.GetType());
      Initialize();
      AddInstanceAuthorizationRules();
      if (!Security.SharedAuthorizationRules.RulesExistFor(this.GetType()))
        AddAuthorizationRules();
    }


because _autorizationRules was marked NonSerialized, the variable wou ld return null value after serialization.
I think if the code to initialize/load autorization rules be moved to Initialize() would solve this problem.

Ricky



dag.oyvind replied on Thursday, November 16, 2006

Thanks guys - I really appreciate you taking the time to look into these things. I was going to upload a simple test-project "proving" the error tomorrow, but  rasupit you nailed the error and got to it before me. Thanks Rocky for spending so much time here - always giving useful and precise answers to both CSLA and general OO-design related questions.

I'm only allowed to work 60% on .NET technology in my company which is really annoying as it breaks the rythm and flow of development. On the other hand - the other 40% is needed keep the money flow (and hence my salary) and customers happy...

 

rasupit replied on Thursday, November 16, 2006

Forgot to attach the test code

ajj3085 replied on Wednesday, November 15, 2006

Joe,

There is a big difference; the C# one will thrown an exception if the user cannot read the property.  The VB one won't, and will return empty string if the user cannot read the property.

I think the normal way is to thrown an exception, and use the ReadWriteAuthorization extender to handle things.

Technically either way works, but typically you'd want to throw, because the user really can't read the property, and your program can determine this before attempting to do the read (via CanReadProperty).

Copyright (c) Marimer LLC