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
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.
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?
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
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.
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
}
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:
I'm kind of leaning toward option 3.
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.
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.
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?
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).
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.
Did you make the required changes to the client and server regarding the serialization configuration?
http://www.lhotka.net/weblog/CSLA4Version45Beta1Released.aspx
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.
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.
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).
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