Csla Light - Couple of questions/issues to report

Csla Light - Couple of questions/issues to report

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


paupdb posted on Thursday, August 28, 2008

I've got a few suggestions that I'd like to run by Rocky and the other Magenic guys regarding CSLA Light.

I'm basing these questions on using the latest 3.6 source code straight out of Subversion (svn://svn.lhotka.net/csla/).

First question: Csla.Security.CslaIdentity

I'm currently subclassing the above to have my own custom Identity, but one thing that irritating is the RolesProperty.
Currently the RolesProperty PropertyInfo field is private in CslaIdentity, and the protected property Roles has no getter.

The problem is that I want to be able to get the Roles list out of the Identity in some special circumstances.

Any chance you can either make the RolesProperty protected in CslaIdentity, or else provide a getter on the Roles property?

If not, I guess I'll just make a copy of CslaIdentity instead of subclass.


Second question: WcfPortalClient and ServiceReferences.ClientConfig

The application I'm building will be deployed at many client sites, and as such the urls relating to the website will be different each time.  Now having to configure the endpoint address in the web.config is fine, but the ServiceReferences.ClientConfig side is embedded in the Silverlight xap file.
Having to unzip the file, update the ServiceReferences.ClientConfig, and rezip the xap is something I want to avoid.

So what I've done is programatically set the WcfProxy.DefaultUrl and WcfProxy.DefaultBinding properties on startup of my Silverlight app:

      //Csla settings - note it is assumed that WcfPortal.svc exists in web application root
      string pageUrl = HtmlPage.Document.DocumentUri.AbsoluteUri;
      string appRootUrl = pageUrl.Substring(0, pageUrl.LastIndexOf('/') + 1);

      ApplicationContext.GlobalContext["AppRootUrl"] = appRootUrl;

      //Set up WcfProxy DataPortal service
      Csla.DataPortalClient.WcfProxy.DefaultUrl = appRootUrl + "WcfPortal.svc";

      //Set WCF message size to support large resultsets
      System.ServiceModel.BasicHttpBinding largeBinding = new System.ServiceModel.BasicHttpBinding();
      largeBinding.MaxReceivedMessageSize = 32000000;
      largeBinding.MaxBufferSize          = 32000000;  

      Csla.DataPortalClient.WcfProxy.DefaultBinding = largeBinding;

      Csla.DataPortal.ProxyTypeName = "Csla.DataPortalClient.WcfProxy, Csla";


I also went so far as to comment out the endpoint configuration in my ServiceReferences.ClientConfig - just to be sure my programmatic settings were being applied.
This all works really well when I access the DataPortal without criteria - i.e. the Csla code runs into WcfProxy.BeginFetch(), which in turn calls WcfProxy.GetProxy() to setup its proxy object.
I noticed the GetProxy method checks the DefaultBinding and DefaultUrl properties and uses these if set.

However, when trying to access the DataPortal with a Criteria object, I found the code was crashing because the endpoint was not defined.
This is because WcfProxy.BeginFetch(object criteria, object userState) is not calling GetProxy() to setup its proxy object.  Instead it simply calls new WcfPortal.WcfPortalClient().
So the DefaultBinding and DefaultUrl are not applied :(

I'm guessing this is a bug - a call to GetProxy() should be being made imo.
Maybe the WcfPortalClient constructor should be private to prevent such cases occurring?  Or maybe move the GetProxy onto WcfPortalClient as a factory method?

Either way can this be fixed?


Other than that, things are rolling on nicely with CslaLight.  Great job Magenic guys.

paupdb replied on Thursday, August 28, 2008

Another minor suggestion: Csla.SingleCriteria<B, C>

I am inheriting from this class in order to have strongly named criteria classes which make the various data portal methods more distinct.  e.g. IDCriteria, NameCriteria etc.

I also have some custom criteria classes that have more than one property, which inherit from CriteriaBase.

CriteriaBase has its CSLA.Net parameterless constructor declared as protected - this is nice for subclassing since I don't have to implement a parameterless constructor and the MobileFormatter works with no issue.

However when subclassing from SingleCriteria, I found I had to explicitly declare parameterless constructors for the CSLA.Net side for MobileFormatter to work; and on top of that I had to call the base SingleCriteria(C value) constructor with a default value (yuk!) in order to get my class to compile:

public class IDCriteria : SingleCriteria<T, int> {
      public IDCriteria(int id) : base(id) { }

      #if SILVERLIGHT
      public IDCriteria() { }
      #else
      private IDCriteria() : base (-1) { }
      #endif


This is because the parameterless CSLA.Net constructor in SingleCriteria is declared as private instead of protected, so I can't inherit it.

Any chance we can get the SingleCriteria CSLA.Net parameterless constructor changed to protected to avoid having to do the above?

paupdb replied on Friday, August 29, 2008

Some more behavior I think may be buggy in Csla.DataPortalClient.WcfProxy<T> (in cslalightcs sln):

As part of a Fetch on one of my business objects, I am setting a value into the ApplicationContext.GlobalContext on the server side (i.e. in the BO DataPortal_Fetch).
I expect that value to then be visible in the Silverlight GlobalContext when the Fetch callback method fires.  However it isn't :(

Now my understanding of the GlobalContext is that it should be the same on both client and server side, since it is serialised both ways with every request. 
So if I set something on the client GlobalContext - I should see it on the server side during a dataportal request (this works as expected), and similarly if I set something into the GlobalContext on the server side during a WcfProxy dataportal fetch, then that should come back to the client side.

I've done some debugging, and I found that the server GlobalContext is indeed coming back from the server through the WcfProxy, however it is never set onto the SL client side ApplicationContext.GlobalContext. 
Instead the GlobalContext received from the server side is deserialised and assigned to property _globalContext on the WcfProxy<T>.

Interestingly, I can access this GlobalContext property from the sender object parameter (eg. o.GlobalContext) in my callback and hence see the server side value I set has come back to the client.
But obviously, having to manually then take o.GlobalContext and set that into the Silverlight client ApplicationContext.GlobalContext seems a little manual to me.

Surely the ApplicationContext.GlobalContext should be automagically set in WcfProxy according to what is deserialised on both the server and client side?
Or are there threading issues?

For reference - an example of the _globalContext being set (but not the ApplicationContext.GlobalContext) from the FetchCompleted method of WcfProxy (the same setting happens in all the other *Completed methods):

    private void proxy_FetchCompleted(object sender, Csla.WcfPortal.FetchCompletedEventArgs e)
    {
      try
      {
        if (e.Error == null && e.Result.ErrorData == null)
        {
          var buffer = new System.IO.MemoryStream(e.Result.ObjectData);
          var formatter = new MobileFormatter();
          T obj = (T)formatter.Deserialize(buffer);
          _globalContext = (ContextDictionary)MobileFormatter.Deserialize(e.Result.GlobalContext);
          OnFetchCompleted(new DataPortalResult<T>(obj, null, e.UserState));

sergeyb replied on Friday, August 29, 2008

This behavior was actually done so by design.  The main reason was that you can send multiple requests at the same time due to asynchronous nature of SilverLight, thus there would never be any guaranty that you global context from one call would not be overwritten from another asynchronous call before you have a chance to check it.  So, instead of resetting global context every time it is exposed on a property (as you correctly noticed) to allow you do any post-call manipulations with it.  Asynchronous data portal in .NET behaves the same way incidentally – it also does not overwrite global context.

 

Sergey Barskiy

Principal Consultant

office: 678.405.0687 | mobile: 404.388.1899

cid:_2_0648EA840648E85C001BBCB886257279
Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: paupdb [mailto:cslanet@lhotka.net]
Sent: Friday, August 29, 2008 8:08 AM
To: Sergey Barskiy
Subject: Re: [CSLA .NET] Csla Light - Couple of questions/issues to report

 

Some more behavior I think may be buggy in Csla.DataPortalClient.WcfProxy<T> (in cslalightcs sln):

As part of a Fetch on one of my business objects, I am setting a value into the ApplicationContext.GlobalContext on the server side (i.e. in the BO DataPortal_Fetch).
I expect that value to then be visible in the Silverlight GlobalContext when the Fetch callback method fires.  However it isn't :(

Now my understanding of the GlobalContext is that it should be the same on both client and server side, since it is serialised both ways with every request.

I've done some debugging, and I found that the server GlobalContext is indeed coming back from the server through the WcfProxy, however it is never set onto the SL client side ApplicationContext.GlobalContext. 
Instead the globalcontext received from the server side is deserialised and assigned to property _globalContext on the WcfProxy<T>.

Interestingly, I can access this globalcontext property from the sender object o.GlobalContext parameter in my callback (and hence see the server side value I set on the GlobalContext).
But obviously, having to manually then set that into Silverlight client ApplicationContext.GlobalContext seems a little manual to me.

Surely the ApplicationContext.GlobalContext should be set according to what is deserialised on both the server and client side?

For reference - an example of the _globalContext being set from the FetchCompleted method of WcfProxy (the same setting happens in all the other *Completed methods):

    private void proxy_FetchCompleted(object sender, Csla.WcfPortal.FetchCompletedEventArgs e)
    {
      try
      {
        if (e.Error == null && e.Result.ErrorData == null)
        {
          var buffer = new System.IO.MemoryStream(e.Result.ObjectData);
          var formatter = new MobileFormatter();
          T obj = (T)formatter.Deserialize(buffer);
          _globalContext = (ContextDictionary)MobileFormatter.Deserialize(e.Result.GlobalContext);
          OnFetchCompleted(new DataPortalResult<T>(obj, null, e.UserState));



paupdb replied on Friday, August 29, 2008

Sergey,

Thanks for the feedback.  I understand what you mean in terms of the GlobalContext design - maintaining consistent client state in the midst of asynchronous calls is tricky.
I can access the GlobalContext property, so thats no major issue :)

Glad to see the other items I reported have been resolved as well :)

Any chance of that protected parameterless constructor on the SingleCriteria (reported in my second post above)?

- Paul

sergeyb replied on Sunday, August 31, 2008

Thanks for your feedback, Paul.

We added paremeter-less constructor to SingleCriteria.  You should be set to go.

 

Sergey Barskiy

Principal Consultant

office: 678.405.0687 | mobile: 404.388.1899

Magenic ®

Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: paupdb [mailto:cslanet@lhotka.net]
Sent: Saturday, August 30, 2008 12:04 AM
To: Sergey Barskiy
Subject: Re: [CSLA .NET] RE: Csla Light - Couple of questions/issues to report

 

Sergey,

Thanks for the feedback.  I understand what you mean in terms of the GlobalContext design - maintaining consistent client state in the midst of asynchronous calls is tricky.
I can access the GlobalContext property, so thats no major issue :)

Glad to see the other items I reported have been resolved as well :)

Any chance of that protected parameterless constructor on the SingleCriteria (reported in my second post above)?


paupdb replied on Sunday, August 31, 2008

You the man Sergey :D

sergeyb replied on Sunday, August 31, 2008

You should thank Rocky – it was his decision.

 

Sergey Barskiy

Principal Consultant

office: 678.405.0687 | mobile: 404.388.1899

Magenic ®

Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: paupdb [mailto:cslanet@lhotka.net]
Sent: Sunday, August 31, 2008 7:00 PM
To: Sergey Barskiy
Subject: Re: [CSLA .NET] RE: RE: Csla Light - Couple of questions/issues to report

 

You the man Sergey :D


sergeyb replied on Friday, August 29, 2008

Thank you for your comments, first of all.

One the first issue, protected get has been added to CslaIdentity.Roles

On the second issue, this is a bug, and it has been fixed.

 

Sergey Barskiy

Principal Consultant

office: 678.405.0687 | mobile: 404.388.1899

cid:_2_0648EA840648E85C001BBCB886257279
Microsoft Worldwide Partner of the Year | Custom Development Solutions, Technical Innovation

 

From: paupdb [mailto:cslanet@lhotka.net]
Sent: Thursday, August 28, 2008 8:11 AM
To: Sergey Barskiy
Subject: [CSLA .NET] Csla Light - Couple of questions/issues to report

 

I've got a few suggestions that I'd like to run by Rocky and the other Magenic guys regarding CSLA Light.

I'm basing these questions on using the latest 3.6 source code straight out of Subversion (svn://svn.lhotka.net/csla/).

First question: Csla.Security.CslaIdentity

I'm currently subclassing the above to have my own custom Identity, but one thing that irritating is the RolesProperty.
Currently the RolesProperty PropertyInfo field is private in CslaIdentity, and the protected property Roles has no getter.

The problem is that I want to be able to get the Roles list out of the Identity in some special circumstances.

Any chance you can either make the RolesProperty protected in CslaIdentity, or else provide a getter on the Roles property?

If not, I guess I'll just make a copy of CslaIdentity instead of subclass.


Second question: WcfPortalClient and ServiceReferences.ClientConfig

The application I'm building will be deployed at many client sites, and as such the urls relating to the website will be different each time.  Now having to configure the endpoint address in the web.config is fine, but the ServiceReferences.ClientConfig side is embedded in the Silverlight xap file.
Having to unzip the file, update the ServiceReferences.ClientConfig, and rezip the xap is something I want to avoid.

So what I've done is programatically set the WcfProxy.DefaultUrl and WcfProxy.DefaultBinding properties on startup of my Silverlight app:

      //Csla settings - note it is assumed that WcfPortal.svc exists in web application root
      string pageUrl = HtmlPage.Document.DocumentUri.AbsoluteUri;
      string appRootUrl = pageUrl.Substring(0, pageUrl.LastIndexOf('/') + 1);

      ApplicationContext.GlobalContext["AppRootUrl"] = appRootUrl;

      //Set up WcfProxy DataPortal service
      Csla.DataPortalClient.WcfProxy.DefaultUrl = appRootUrl + "WcfPortal.svc";

      //Set WCF message size to support large resultsets
      System.ServiceModel.BasicHttpBinding largeBinding = new System.ServiceModel.BasicHttpBinding();
      largeBinding.MaxReceivedMessageSize = 32000000;
      largeBinding.MaxBufferSize          = 32000000;  

      Csla.DataPortalClient.WcfProxy.DefaultBinding = largeBinding;

      Csla.DataPortal.ProxyTypeName = "Csla.DataPortalClient.WcfProxy, Csla";


I also went so far as to comment out the endpoint configuration in my ServiceReferences.ClientConfig - just to be sure my programmatic settings were being applied.
This all works really well when I access the DataPortal without criteria - i.e. the Csla code runs into WcfProxy.BeginFetch(), which in turn calls WcfProxy.GetProxy() to setup its proxy object.
I noticed the GetProxy method checks the DefaultBinding and DefaultUrl properties and uses these if set.

However, when trying to access the DataPortal with a Criteria object, I found the code was crashing because the endpoint was not defined.
This is because WcfProxy.BeginFetch(object criteria, object userState) is not calling GetProxy() to setup its proxy object.  Instead it simply calls new WcfPortal.WcfPortalClient().
So the DefaultBinding and DefaultUrl are not applied :(

I'm guessing this is a bug - a call to GetProxy() should be being made imo.
Maybe the WcfPortalClient constructor should be private to prevent such cases occurring?  Or maybe move the GetProxy onto WcfPortalClient as a factory method?

Either way can this be fixed?


Other than that, things are rolling on nicely with CslaLight.  Great job Magenic guys.

Copyright (c) Marimer LLC