Troubles migrating to CSLA 4.5 and Silverlight 5

Troubles migrating to CSLA 4.5 and Silverlight 5

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


Dane posted on Saturday, November 03, 2012

I'm quite certain all my troubles are related to the breaking changes introduced in CSLA 4.5 for Silverlight 5 but I've read all the posts I can find, I've updated the config namespaces from Silverlight to Mobile but I'm still missing something.

My project has its primary UI in Silverlight. It's been functional for quite some time under CSLA 4 and Silverlight 4. I'm working on bringing it forward to CSLA 4.5 / Silverlight 5 and I'm having significant issues with DataPortal usage.

As an example I have a class which inherits ReadOnlyListBase. It has a static method GetListAsync.

public static void GetListAsync(MyCriteria criteria, EventHandler<DataPortalResult<MyList>> handler)
{
    DataPortal<MyList> dataPortal = new DataPortal<MyList>();
    dataPortal.FetchCompleted += handler;
    dataPortal.BeginFetch(criteria);
}

I've then implemented a DataPortal_Fetch override which is only visible outside of Silverlight, (#if !SILVERLIGHT preprocessor). DataPortal_Fetch then accesses my DAL and loads the list. Prior to the version upgrade the DataPortal_Fetch method was being called on the server but now it is never being called. The callback shows a DataPortalResult with an Error as follows:

e.Error {Csla.DataPortalException: DataPortal.Fetch failed (Invalid operation - fetch not allowed) ---> Csla.Reflection.CallMethodException: MyList.DataPortal_Fetch method call failed ---> System.NotSupportedException: Invalid operation - fetch not allowed
   at Csla.ReadOnlyListBase`2.DataPortal_Fetch(Object criteria)
   at lambda_method(Closure , Object , Object[] )
   at Csla.Reflection.MethodCaller.CallMethod(Object obj, DynamicMethodHandle methodHandle, Boolean hasParameters, Object[] parameters)
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Csla.Server.SimpleDataPortal.<Fetch>d__6.MoveNext()
   --- End of inner exception stack trace ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Csla.DataPortal`1.<BeginFetch>d__1e.MoveNext()
}    System.Exception {Csla.DataPortalException}
I'm obviously not understanding something about the changes. Any help you can provide would be appreciated.
Thanks

skagen00 replied on Monday, November 05, 2012

I'm running into a case with creating a BO where my DP_Create code on the server isn't getting called too.

It ends up determining that it should RunLocal=true and then just does a DP_Create locally in SL rather than going to the server.  It's almost like it's reflecting SL code to find a matching DP_Create and when it doesn't find it it decides to ignore the fact it might exist in the .NET server code.

skagen00 replied on Monday, November 05, 2012

DataPortalT.cs - DoCreateAsync

        var method = Server.DataPortalMethodCache.GetCreateMethod(objectType, criteria);

This ends up looking for a DataPortal_Create and ends up finding the Csla.Core.BusinessBase's implementation of DataPortal_Create which is marked as [RunLocal].  As a result, it never ends up getting to the server's DataPortal_Create(criteria) which I've implemented in my own class to provide some data access behind DataPortal_Create.

Is this a bug or something we need to work around - and if so, what is the workaround?

skagen00 replied on Monday, November 05, 2012

Here is a workaround - not elegant but I think it'd work fairly consistently - if I add this in my own BusinessBase (from Csla.BusinessBase), the GetCreateMethod ends up locating this on the server side and then going to the server.  Hoping perhaps to get some guidance on the best approach to take.

#if SILVERLIGHT

        /// <summary>
        /// Create a non-[RunLocal] DataPortal_Create to allow Silverlight to not catch the [RunLocal] marked Csla.Core.BusinessBase's
        /// DataPortal_Create.
        /// </summary>
        protected void DataPortal_Create(object criteria)
        {
           
        }

#endif

 

JonnyBee replied on Tuesday, November 06, 2012

Hi,

It is a braking change in CSLA 4.5 that all versions now use the standard DataPortal implementation.

Which means that SL DataPortal now honors [RunLocal] attribute and that ProxyModes enum (Auto/LocalOnly) no longer exists. Pre 4.5 the SL DataPortal would not check for the existence of RunLocal attribute.

I believe this is part of of issue 1073 : Make Silverlight and WinRT use the standard .NET data portal implementation

Execisting code in SL / WinRT that used the ProxyModes.LocalOnly must be updated to use the RunLocal attribute and you must also verify that RunLocal is only applied to methods that should run locally.  

skagen00 replied on Tuesday, November 06, 2012

Hi Johnny -

I'm not entirely sure you followed the question. I have a server side dataportal_create method in my class, but it fails to get called *because* it's finding a dataportal_create marked as runlocal in Csla.Core.BusinessBase.

What is the appropriate route? Is it to basically have DataPortal_Create/etc that look like the following?

protected void DataPortal_Create(object criteria)

{

#if !SILVERLIGHT

Data access code

#endif

}

RockfordLhotka replied on Tuesday, November 06, 2012

This sounds like a bug.

I'm not entirely sure how to resolve the bug without creating a breaking change.

If you don't implement DP_Create at all, the desired behavior IS to have it run locally. So the current implementation is "correct".

Solutions that come to mind:

  1. Remove the DP_Create from the base class - forcing all classes to implement the method excplicitly, and breaking most current apps
  2. Conditionally remove the [RunLocal] attribute from the DP_Create in the base class on Silverlight, forcing all SL apps to default to calling the server even if there isn't a DP_Create on the server
  3. Rename this bug as a feature, and provide guidance on creating a dummy DP_Create on SL that doesn't have [RunLocal]

I'm kind of leaning toward option 3.

 

Dane replied on Tuesday, November 06, 2012

I'm still pretty confused. I don't use the RunLocal attribute at all and I have no DataPortal_XYZ methods in my Silverlight code (they're all removed by pre-processors). I'm using the Encapsulated Invocation data access model and my DataPortal_XYZ methods are invoking my DAL only on the .NET side, there's no access or knowledge of this layer from within Silverlight.

JonnyBee replied on Tuesday, November 06, 2012

I believe the CSLA BusinessBase base class DataPortal_Create method has the RunLocal attribute - so unless you override the default implementation it will be called locally. 

Dane replied on Tuesday, November 06, 2012

Specifically I'm running into it with the DataPortal_Fetch method and I'm getting the exception I posted at the top of this thread, i.e "Invalid operation, fetch not allowed". Are you saying I need to create a Silverlight side DataPortal_Fetch implementation that's empty?

skagen00 replied on Tuesday, November 06, 2012

I don't have any DataPortal_Fetch signatures in my SL and I'm fine.  I suspect you have something else going on.

I found the [RunLocal] issue w/ DP_Create by stepping through the debug of CSLA.  Is the signature of your DP_Fetch identical to what's being passed? Maybe it has become less forgiving in terms of type-matching? (Just a thought).

(Sorry to have hijacked your thread with that DP_Create issue which certainly sounds different from what you're experiencing)

Do you believe it's getting to the server? I sometimes would put breakpoints in my CompressedHost/Utility class to just give myself the knowledge that such-and-such was hitting the server.  (Or a breakpoint in the CSLA server side data-portal class). 

Dane replied on Tuesday, November 06, 2012

No, I don't believe it's reaching the server at all. I may have issues with all other DataPortal_XYZ methods as well but my app first failed on a Fetch so that's where I started to focus. The DataPortal_Fetch virtual method is defined to take a criteria of type object so the type is definitely appropriate. At this point I'm at a complete loss. I'm sure it has something to do with changes in either CSLA 4.5 or Silverlight 5 because it's been working without issue for months under CSLA 4 and Silverlight 4.

RockfordLhotka replied on Tuesday, November 06, 2012

Did you make the required changes to the client and server regarding the serialization configuration?

http://www.lhotka.net/weblog/CSLA4Version45Beta1Released.aspx 

Dane replied on Tuesday, November 06, 2012

If you are referring to the item about the change in namespace yes. Though the example has a typo in it. That post says to replace "Silverlight" with "Mobile" in the Csla.Server.Hosts.IWcfPortal but there's no "Silverlight" in that example. In my code I found Csla.Server.Hosts.Silverlight appearing in three places, the contract element of the service endpoint and the name element for that service defined in the web.config and again in the Service element of the WcfPortal.svc file. I replaced the "Silverlight" with "Mobile" in both files and made sure the name elements in the two locations matches.

This did have wondering however as the contract is Csla.Server.Hosts.Mobile.IWcfPortal and, exploring around with Intellisense, I could not find that interface at that location. I've only found IWcfPortal at Csla.WcfPortal.IWcfPortal. I tried updating the contract but with no luck. I also found that my ServiceReferenece.ClientConfig file had a contract WcfPortal.IWcfPort which differs from that in the web.config file. I tried updating both of these with the Csla.Server.Hosts.Mobile.IWcfPortal and then the Cals.WcfPortal.IWcfPortal but again without any luck in either case. 

I don't believe the other three bullet items in that post are relevant to my project.

UPDATE: I found that Csla.WcfPortal.IWcfPortal is the only definition for that interface in Silverlight but I found that the .NET assembly has both Csla.Server.Hosts.Mobile.IWcfPortal and Csla.Server.Hosts.IWcfPortal. I haven't found any combination of these however that is getting me past the original exception.

Dane replied on Wednesday, November 07, 2012

I went ahead and tried adding an empty protected override DataPortal_Fetch to the Silverlight code of my read only list class. It now runs without an exception but, of course, returns no data because it never calls the service. The initial exception I included above (and the fact it goes away when I include a Silverlight implementation of that method) seems to indicate that CSLA is demanding such an implementation or, perhaps that is being caused by the Silverlight client's inability to communicate with the server. I'm at a complete loss what to try next. There appears to be something wrong with the communication between the client and server components through the WCF portal but I've about exhausted the avenues I can think of to test.

RockfordLhotka replied on Wednesday, November 07, 2012

Have you set the client side data portal proxy type to WcfProxy? It sounds like the data portal is configured to run in local mode (the default).

Dane replied on Thursday, November 08, 2012

Thanks Rocky. That seems to have put me back on track.

I'd added this to app.xaml.cs when I first created the Silverlight project.

private void Application_Startup(object sender, StartupEventArgs e)
{
    //Csla.DataPortal.ProxyTypeName = "Local";
    this.RootVisual = new MainPage();
}

I commented out the Local setting because I didn't want local and then I forgot to remove it. If I understand correctly Local is now default therefore I need to set it explicitly to return to non-Local.

I went back and re-read the section on Silverlight proxy in Using CSLA 4 Data Portal Configuration and with the new info pieced together what seems to be a working solution. I replaced the commented line above with this.

Csla.DataPortal.ProxyTypeName = typeof(Csla.DataPortalClient.WcfProxy).AssemblyQualifiedName;
Csla.DataPortalClient.WcfProxy.DefaultEndPoint = "BasicHttpBinding_IWcfPortal";

The endpoint name was copied from the documentation back when I first created the ServiceReferences.ClientConfig file. It appears these were the default prior to 4.5?

Anyway, I'm past that hurdle and continuing to convert the rest of the app.

Thanks again to Rocky, Jonny and skagen for your assistance.

P.S. Rocky: Are there future plans to update the Using CSLA 4 ebooks for the 4.5 world? I know time must be a huge issue but I thought I'd ask.

Copyright (c) Marimer LLC