Sunday, April 24, 2011

Technical Note: Silverlight code for CSLA 4



This note was written for CslaGenFork project.

Summary
When using a Silverlight client, your application must always use an application server. Because of Silverligt restrictions, the business objects library DLL must be different on the Silverlight client and on the application server. In this post we will see what are those restrictions, the solutions to the issues they raise and how to use the generated code. In fact this post summarizes what needs to be changed in order to run CSLA code under Silverlight.

N.B. - CSLA Silverlight changes are numbered and marked in bold.


I - Silverlight restrictions

Due to security reasons, there are a lot of things you can't do on Silverlight code. For what CSLA cares, there are three things you can't do under Silverlight:
1. Access a database server, namely SQL Server
2. Make synchronous calls to the application server
3. Use reflection on non-public members of other classes (one class can't use reflection on private members of another class)
Let's analyse the CSLA solutions for each one of these restrictions.


II - CSLA solutions

1. The Silverlight client must receive and send the data through the application server.

CSLA.NET automatically takes care of routing all DataPortal calls to the application server. If CSLA.NET doesn't find a suitable DataPortal method in the client code, it routes the call to the application server.

1.1. DataPortal_Create is a special case because most of the times you want it to run locally.
Regular CSLA uses the attribute [RunLocal] to mark DataPortal methods that must be run on the client (client side DataPortal concept). When DataPortal_Create exists, it's a strong candidate to run client side unless you need to load object default values from the database.
On Silverlight, DataPortal client side methods accept an extra parameter DataPortal.ProxyModes.LocalOnly that forces the DataPortal code to run locally.

1.1.1. On the Silverlight client, to run DataPortal_Create locally, there must be a specific asynchronous DataPortal_Create method.
The Silverlight signature is different from the equivalent method that may exist on non-Silverlight clients.
The Silverlight signature looks like this:
public override void DataPortal_Create(Csla.DataPortalClient.LocalProxy.CompletedHandler handler)
The regular CSLA signature looks like this:
protected override void DataPortal_Create()

1.1.2. On the Silverlight client, there must be a specific asynchronous factory method that invokes DataPortal.BeginCreate passing that extra parameter.
The Silverlight invocation looks like this:
DataPortal.BeginCreate(callback, DataPortal.ProxyModes.LocalOnly);
The regular CSLA asynchronous invocation looks like this:
DataPortal.BeginCreate(callback);

N.B. - All DataPortal_XYZ methods can run locally. This might be useful in scenarios where the Silverlight client is using REST or SOAP services to interact with the database.

2. The Silverlight client must invoke the application server asynchronously.
CslaGenFork can generate synchronous or asynchronous code. When generating Silverlight, asynchronous code is always generated. This includes:

2.1. Asynchronous LazyLoad for properties (may be synchronous or asynchronous under regular CSLA, but never both at the same time).
If both options for synchronous and asynchronous code generation are set, under Silverlight the getter code will be asynchronous code and synchronous code for non-Silverlight environments.

2.2. Asynchronous Factory methods (may be both synchronous and asynchronous under regular CSLA).
The Silverlight and synchronous factory methods are called asynchronously and they invoke the DataPortal methods asynchronously. CGF generates synchronous and asynchronous code according to the settings (no conditions here).

N.B. - Business rules that must get data from the application server must also run asynchronously. That's a problem that must be addressed by the rule developer but beyond CslaGenFork concerns.

3. On the Silverlight client, all usual private or protected member must be public.
This issue is solved with code like this:
#if SILVERLIGHT
    [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
    public ...
#else
    private ...
#endif
The EditorBrowsable attribute intends to hide the member from InteliSense (and from the developer) as a way not to encourage its use.
This issue shows on the following cases:

3.1. public PropertyInfo declaration (private under regular CSLA)

3.2. public class constructor (private under regular CSLA)

3.3. public AddObjectAuthorizationRules (protected under regular CSLA)

3.4. public DataPortal methods (some are private or protected under regular CSLA)


III - What is generated and how to use it

As of November 2011, CslaGenFork generates both Encapsulated Implementation and Encapsulated Invoke DataPortal models (according to Using Csla 4 ebooks classification). The usual 2 files are generated: the .Designer file and the extended file.
The extended file is where CGF puts the commented partial method implementation of the Pseudo Event Handlers. Note that all these methods run server side. If you are regenerating a project, you should put a pair of conditional compilation directives around the whole region:
#if !SILVERLIGHT
#endif
In the .Designer file you will also find a lot of directives just like the above. According to the compilation symbols defined in your project, some parts of the code are ignored. This way you take the same source code and get two different DLLs: one for the Silverlight client and another for the application server. The later can also be used on:
  • WPF client application
  • WPF application server
  • ASP.NET application server
N.B. - For Windows Forms there are other issues (ListBase versus BindingListBase inheritance) that prevent the use of the same DLL.

One way to accomplish this goal is to make a copy of all source files and put each copy in a different project: one on a Silverlight Class Library project and another on a Windows Class Library project.

A more usual approach does the same thing except you don't copy the files but use kind'a shortcuts. Say your main project is the Windows Class Library. Put the files there and build it as usual. On the Silverlight Class Library project, add all folders needed by the project. After that, go to each of the folders and add existing items. Now that's the magic part: navigate to your main project files, select the files and instead of clicking Add, click the small down arrow and click Add As Link.

Tiago Freitas Leal

Links:
CSLA.NET
CslaGenFork