| |
 |
|
 |
|
Rockford LhotkaRockford Lhotka
The second part of the DNR TV show covering CSLA .NET 3.5.1 is now online. Here are
links to both parts:
As always, you can download CSLA .NET from
here.
Published Wed, 27 Aug 2008 13:03:12 GMT
by Rockford Lhotka
I've been thinking a lot about the future of CSLA and of .NET, and I'd like thoughtful
input.
The .NET platform is, for lack of a better word, fragmenting. We have .NET, the .NET
client-only framework, .NET Compact Framework, Silverlight and probably others. All
of these are ".NET", but they are all different.
CSLA will soon support .NET and Silverlight. It sort of supports the Client-Only Framework
now, but it was just pointed out to me that while it works, there are compiler warnings
in this scenario.
For the past 6 or so years, I've been maintaining all my code twice, once in VB and
once in C#. This has never been fun, and wouldn't have been possible at all without
help from my colleague Brant Estes and from the Instant
VB and Instant C# tools. Even with this, the reality is that I have to do everything
twice, and test everything twice.
(yes, I have a unified set of unit tests, but there's a ton of manual testing around
data binding in Web Forms, Windows Forms, WPF and now Silverlight that can't be automated)
But now I'm faced with a truly horrific future scenario. CSLA .NET, CSLA Light, possibly
CSLA CF and CSLA Client-Only. Four versions of the framework. Two languages for each
version. Fixing a single bug requiring changing and testing 8 implementations.
Clearly that's not realistic. Not only would it eliminate any fun, but it just isn't
practical. I doubt it would be practical even if I made CSLA a commercial product...
Of course I can cut the complexity in half by maintaining the framework in only one
programming language.
This is a touchy thing of course. I was thinking Modula II, but I can't find a good
compiler for .NET... :)
Seriously though, the clear choice would be to maintain the framework in C#, at which
point I run the risk of alienating the VB community. You might argue I should maintain
the framework in VB, but (for better or worse) that would almost certainly alienate
a much bigger constituency.
The really important thing is that the framework would support both VB and C#.
Regardless of what I do or don't do inside the framework, it can be used by either
language (and other languages for that matter) equally well. After all, most of the
.NET framework is written in just one language, yet it is used by everyone equally.
Right now CSLA Light is only in C#, though I'm testing in both VB and C# to make sure
it supports both equally. I haven't tried, but I imagine you can use it from Ruby
or Python too, since both of those languages work in Silverlight too.
Another alternative would be to solicit help from the community. For example, perhaps
one or more people would be willing to help keep the VB version of the framework in
sync over time. That has its own complications, but might be a worthy solution.
This also ties into my book projects. I'm working on Chapter 16 of 21 for Expert
C# 2008 Business Objects right now. As with the previous editions, most of the
book is focused on the design and creation of the framework itself, with less focus
on how to use the framework.
I think a lot of people would actually prefer a book on how to use the framework,
not caring so much how and why I implemented things internally. And I'd enjoy writing
that book (in VB and C# editions). But as it is, I'm looking at 2-3 months of work
to get CSLA .NET 3.6 working in VB (which will have to wait until the current book
is done), then a couple months to get the Expert VB 2008 Business Objects book
done. That's 4-5 months where I could write a book on how to use the framework. Or
perhaps a series of highly focused ebooks. Or something along that line.
I haven't reached a decision, I'm just thinking long and hard. Thoughtful input is
welcome, thanks!
Published Wed, 20 Aug 2008 21:15:50 GMT
by Rockford Lhotka
Magenic a couple fall community events they are
involved in organizing and/or sponsoring.
In Chicago there's the Codeapalooza event
coming up on September 6. This is sponsored by Magenic and will have speakers from
the whole community. It sounds like Justin Chase will be speaking at the event, talking
about UnitDriven and the unit testing
issues we've been facing while building CSLA Light (Justin is on the CSLA Light dev
team).
Then in Minneapolis there's the 5th
Twin Cities Code Camp on October 11. I'll be speaking at this event, talking about CSLA
Light and CSLA .NET 3.6. By that point
CSLA Light should be well into its beta phase and will be a lot of fun to show (and
see).
Published Tue, 19 Aug 2008 19:43:30 GMT
by Rockford Lhotka
One of the features we added in CSLA Light was the idea of a "factory" or "observer"
object - kind of like a police sobriety checkpoint - that can be called when any data
portal request comes in from the client. This is a server-side object that can examine
or manipulate the business object or criteria object sent from the client, before
any other server-side processing is allowed.
I liked the idea so much that I decided to put it into the CSLA .NET 3.6 data portal
too. Which turned out to be much harder than I expected...
Of course I don't want to break compatibility with the current data portal behavior,
so it must continue unchanged.
But if you put the ObjectFactory attribute on your business class, that should redirect
the data portal to create an instance of the factory object you specify, and to route
all data portal calls to methods on that object instead of calling DataPortal_XYZ
as normal.
For example:
[ObjectFactory("Factories.MyFactory,Factories")]
[Serializable]
public class CustomerEdit : BusinessBase<CustomerEdit>
This tells the data portal to not call any DataPortal_XYZ methods, but instead
to create an instance of a MyFactory object and to call methods named Create(), Fetch(),
Update() and Delete() on that object instead.
This is extensible in numerous ways. You can specify the names of the four methods.
You can provide a "factory loader" so that string parameter passed to ObjectFactory
can be interpreted differently - how ever you'd like. The default factory
loader treats that string like an assembly qualified type name, because I think that's
what most people will want.
So you have to create MyFactory:
namespace Factories
{
public class MyFactory
{
[RunLocal]
public object Create()
{
// create object here
return obj;
}
public object Fetch(SingleCriteria<CustomerEdit, int> criteria)
{
// retrieve object here
return obj;
}
[Transactional(TransactionTypes.TransactionScope)]
public object Update(CustomerEdit obj)
{
// insert/update/delete object here
}
[Transactional(TransactionTypes.TransactionScope)]
public void Delete(SingleCriteria<CustomerEdit, int> criteria)
{
// delete data here
}
}
}
Normal method overloading rules apply to these methods. So you can have multiple Fetch()
methods with different criteria object types and the data portal will call the right
method. This is exactly the same behavior as with DataPortal_Create(), DataPortal_Fetch()
and DataPortal_Delete() today.
Notice that the RunLocal and Transactional attributes can be applied to these methods
just like they would have been applied to the DataPortal_XYZ methods.
RunLocal is interesting though, because it obviously can only work if you deploy your
factory assembly to the client along with the business assembly. In most cases I expect
people won't choose to do that, and so won't be able to use RunLocal.
The default for Transactional is to use Manual transactions. In other words, if you
don't use the attribute it is entirely up to your factory method to handle any transaction
details.
It is very important to realize that when using this factory model, the data
portal does virtually nothing for you. It doesn't automatically call MarkOld(), MarkNew()
or MarkAsChild(). You assume total responsibility for creating and initializing
the business object graph, and for setting the state of all objects in the graph.
Published Fri, 15 Aug 2008 20:16:41 GMT
by Rockford Lhotka
WPF has this cool concept called a data provider control. CSLA .NET includes a custom
data provider control that enables nearly codeless forms (if you don't count XAML
as code), by supporting the following functions purely through XAML:
-
Create/Fetch an object
-
Save an object
-
Cancel changes to an object
-
Add a new item to a list (if the object is a collection)
-
Remove an item from a list (if the object is a collection)
Silverlight doesn't have a data provider concept, nor does it have commanding.
Which means that you need to write code behind the form for all of those actions.
So in CSLA Light we're investigating the possibility of some sort of data provider
control to fill in this gap. The idea is that you'd be able to declare a CslaDataProvider
as a resource on your form and use it as a data source, and as a handler for the save/cancel/add/remove
operations.
This also means we need a way, through XAML, to route things like a button's Click
event to methods on CslaDataProvider (because there's no commanding in Silverlight).
At a prototype level what we've got is something like this. The CslaDataProvider goes
in the Resources section just like it would in WPF:
<UserControl.Resources>
<csla:CslaDataProvider x:Key="MyData"
ManageObjectLifetime="True" />
</UserControl.Resources>
Some WPF concepts don't make sense. For example, IsAsynchronous is useless because
in Silverlight it will be asynchronous. Others, like IsInitialLoadEnabled
might make sense, we're not entirely sure yet.
Your page XAML then uses this as a data source. But where WPF does some magic behind
the scenes for data providers, Silverlight doesn't know about data providers. So you
have to deal with this yourself when setting the DataContext:
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource MyData},
Path=Data}">
The DataContext is set to use the Data property of the data provider, because that's
where the actual business object is found. In WPF this indirection is hidden automatically,
but in Silverlight you have to do it yourself.
In the Form_Loaded() method in code behind you call your business object's factory
method. But rather than handling the async callback yourself, you allow the data provider
to handle it:
private void Form_Loaded(...)
{
var dp = (CslaDataProvider)this.Resources["MyData"];
CustomerEdit.GetCustomer(123, dp.FetchCompleted);
}
I hope to eliminate even this bit of code behind (at least for parameterless create/fetch
scenarios), but the current prototype does require it. The CslaDataProvider exposes
a set of async handlers for create and fetch operations.
When the async request completes, the CslaDataProvider object's Data property changes
to reflect the new business object. That causes a PropertyChanged event, which in
turn causes data binding to refresh the UI so the user sees the data.
That gets us pretty close to the WPF data provider capability of creating or retrieving
an object. But the Csla.Wpf.CslaDataProvider goes further, and so the plan is for
the Csla.Silverlight.CslaDataProvider to also go further.
The data provider object has methods like Save() and Cancel() that can be called to
save the object or cancel changes. The trick then, is how to get UI elements, like
a button, to call those methods when Silverlight has no commanding or relative binding.
At the moment we're addressing this with an InvokeMethod object that exposes some
attached properties. So you can create a Button like this:
<Button csla:InvokeMethod.Resource="{StaticResource MyData}"
csla:InvokeMethod.MethodName="Save"
csla:InvokeMethod.TriggerEvent="Click"
Content="Save"
/>
This tells the InvokeMethod object to listen for a Click event from the Button. When
it gets a Click event, it invokes the Save() method on the MyData resource. Any RoutedEventHandler
or EventHander event can be handled, which should cover nearly all events from any
type of UI control. The Button control's Click event, for example, is a RoutedEventHandler,
as are most Silverlight control events.
(this is only possible btw, thanks to the relaxed delegate support in the current
versions of C# and VB)
The same technique will work to call Cancel(), and presumably AddNewItem() and RemoveItem()
at some point (though with RemoveItem() we'll also need a way to pass in a parameter
value to specify the item to be removed).
The result is that we're pretty close, at least in concept, to the WPF data provider
model, and we avoid the need to write code behind the form for common events like
save, cancel, add new and remove.
Published Wed, 13 Aug 2008 17:03:32 GMT
by Rockford Lhotka
In Part
1 of this series I discussed the overall architecture and structure of a CSLA
Light application. In Part
2 I walked through the creation of a simple editable
root business object that supports both the 3-tier and 4-tier mobile object models.
In Part 3 I discussed
the creation of the Silverlight UI.
In this post I will discuss the configuration of the web/app server and the data portal.
All Silverlight applications require a web server, because the Silverlight runtime
is hosted in a web page, and the user has to get the web page from a web server (I'm
sure there are exceptions to this rule - but in practicality, nearly all Silverlight
apps will be downloaded from a web server).
When using CSLA Light in a 3- or 4-tier client/server model, the CSLA Light client
application will communicate with a .NET application server. That application server
might be the same server from which the Silverlight app was downloaded, or a different
server. It might be hosted in ASP.NET or WAS or a custom Windows Service.
The CSLA Light data portal uses WCF as its network transport, and so the only requirement
is that the server be reachable from the client using the Silverlight WCF implementation.
CSLA .NET 3.6 includes a special data portal host designed to work with a CSLA Light
client. Remember that the CSLA Light data portal is not only going across the network,
but it is communicating between the .NET and Silverlight platforms. So while this
data portal is functionally similar to the .NET data portal, it has to do some extra
work thanks to the cross-platform nature of the scenario.
Configuring the Server
The simplest server configuration is to put the data portal host in the same virtual
root as the Silverlight application itself, and that's what I've done in this example.
When the Silverlight application was created, Visual Studio automatically created
a host web application project. What I've done is added a WcfPortal.svc file, some
web.config entries and a reference to Csla.dll to this project:
The WcfPortal.svc file is a typical WCF service file:
<% @ServiceHost Service="Csla.Server.Hosts.Silverlight.WcfPortal" %>
It references the WcfPortal class that contains the data portal host designed to work
with CSLA Light. Notice that it comes from the Csla.Server.Hosts.Silverlight namespace.
The web project references Csla.dll, and of course also references Library.dll from
the Library.Server project. Remember that Library.Server is a .NET Class Library
project containing all the business classes, while Library.Client is a Silverlight
Class Library project. Since the web server is running .NET, it needs to reference
the .NET assembly.
Finally, the web.config file contains the WCF configuration necessary for the WcfPortal
to be available to clients:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="WcfPortalBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="WcfPortalBehavior"
name="Csla.Server.Hosts.Silverlight.WcfPortal">
<endpoint address=""
binding="basicHttpBinding"
contract="Csla.Server.Hosts.Silverlight.IWcfPortal">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
</service>
</services>
</system.serviceModel>
This is pretty standard WCF configuration, defining an endpoint for the IWcfPortal
interface from Csla.Server.Hosts.Silverlight. Notice that the name of the service
(Csla.Server.Hosts.Silverlight.WcfPortal) matches the name of the service specified
in the svc file.
At this point the app server is configured and ready to go.
Configuring the Client
In Silverlight, WCF gets its configuration from a file called ServiceReferences.ClientConfig.
In Beta 2 I've found that WCF doesn't consistently read this file, but it does work
in the sample app I'm discussing here, and I assume Microsoft will fix whatever is
causing the file to sometimes be ignored.
The config file looks like this:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IWcfPortal"
maxBufferSize="65536"
maxReceivedMessageSize="65536"
receiveTimeout="10" sendTimeout="10">
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://localhost:1406/SimpleAppWeb/WcfPortal.svc
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IWcfPortal"
contract="Csla.WcfPortal.IWcfPortal"
name="BasicHttpBinding_IWcfPortal" />
</client>
</system.serviceModel>
</configuration>
Again, pretty standard-looking WCF configuration like you'd probably find in an app.config
file. It sets up a client endpoint for IWcfPortal, and provides the URL address to
the server.
You can optionally override the configuration in your code by setting two static properties:
Csla.DataPortalClient.WcfProxy.DefaultUrl = "http://localhost/root/WcfPortal.svc"
Csla.DataPortalClient.WcfProxy.DefaultBinding = myBinding
I've been using the config file approach for the most part, but there are certainly
scenarios where you might need to switch to different app server addresses in a more
dynamic manner.
Finally, your application can specify whether the data portal should run in local
or remote mode by setting a property:
Csla.DataPortal.ProxyTypeName = "Csla.DataPortalClient.WcfProxy, Csla"
By default the data portal is configured for remote mode, using WCF, and so the sample
app doesn't do any configuration of the ProxyTypeName. But if you did want to run
the data portal in local mode, you'd set the ProxyTypeName to "Local":
Csla.DataPortal.ProxyTypeName = "Local"
Then the DataPortal_XYZ methods would run on the Silverlight client and the data portal
won't interact with an app server at all. That is particularly useful if you want
to directly call remote services from your Silverlight client, and the primary scenario
in our design is to support the use of .NET Data Services (code name Astoria) - but
that is a topic for another sample app.
In this sample app the only configuration required is in ServiceReferences.ClientConfig.
The data portal defaults to the correct settings to use the values in this file to
communicate with a CSLA .NET application server.
As you can see, configuring the application server and client to use the CSLA Light
data portal is not difficult. The data portal relies on standard WCF configuration
on both ends of the network connection, allowing you to exploit the power and flexibility
of WCF to access the mobile object capabilities provided by the data portal.
Published Tue, 12 Aug 2008 15:05:57 GMT
by Rockford Lhotka
There are many code generation tools and templates available for CSLA .NET. You can
find a list here
http://www.lhotka.net/cslanet/codegen.aspx
Over time I expect to blog about several of the options, usually as they release new
versions or updated templates.
One of the more robust options is CodeBreeze,
from Miguel Castro. Miguel just released a new version of his product, which includes
completely refreshed CSLA .NET code-gen templates.
Miguel is also the author of the CSLA .NET training class from Dunn training, so he
knows CSLA inside and out. He's put up a forum to support CodeBreeze at http://community.steelbluesolutions.com.
Published Mon, 11 Aug 2008 21:33:03 GMT
by Rockford Lhotka
In Part 1 of this
series I discussed the overall architecture and structure of a CSLA Light application.
In Part 2 I walked
through the creation of a simple editable
root business object that supports both the 3-tier and 4-tier mobile object models.
In this post I want to cover the Silverlight UI. Following the theme of this walkthrough,
the UI will be very simple so I can illustrate the basic requirements and concepts.
At this point in time we're working on UI support for authorization, and I'll discuss
it when we have something to show.
UI XAML
The XAML for the UI is not complex, but does have some interesting differences from
typical WPF XAML:
<UserControl x:Class="SimpleApp.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:csla="clr-namespace:Csla.Wpf;assembly=Csla"
Width="800" Height="600" Loaded="UserControl_Loaded">
<Grid x:Name="LayoutRoot" Background="Beige">
<StackPanel Margin="5,5,5,5">
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<TextBlock Width="100" Text="Id" />
<TextBox Width="150" Text="{Binding
Id, Mode=TwoWay}" />
<csla:PropertyStatus Property="Name"
Source="{Binding}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<TextBlock Width="100" Text="Name" />
<TextBox Width="150" Text="{Binding
Name, Mode=TwoWay}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<TextBlock Width="100" Text="Status"
/>
<TextBlock Width="150" Text="{Binding
Status}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,5,5,5">
<Button Width="50" HorizontalAlignment="Left"
Margin="5,5,5,5"
Click="Button_Click"
Content="Save" />
<Button Width="50" HorizontalAlignment="Left"
Margin="5,5,5,5"
Click="Delete_Click"
Content="Delete" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
The primary difference is that the Mode=TwoWay expression is required to
get two-way data binding. In WPF the default is two-way, and in Silverlight it appears
to be one-way.
Also, in WPF I would have created a Style for the StackPanel controls to pull the
Margin property into a Resources block. While I was able to get a Style working for
a TextBlock or TextBox, it would not work for StackPanel. Could be something I did,
or a bug or missing feature in Silverlight... Certainly the XAML to do that from WPF
doesn't work in Silverlight.
In WPF I probably would have used a CslaDataProvider control as my binding source.
The WPF data provider control model is really nice, and I use it all the time. Unfortunately
Silverlight doesn't have a data provider concept, and so obviously that's not here.
Notice the PropertyStatus control from CSLA .NET. It is in the Csla.Wpf namespace,
but will be moving to Csla.Silverlight. PropertyStatus handles both validation and
authorization on a per-control basis, and is responsible for the error icon and tooltip
in the UI. It also shows warning and information severity broken rules, and so is
more comprehensive than the ErrorProvider in Windows Forms or the error support in
WPF.
Creating or Retrieving the Object
When this form is loaded, the business object is either created or retrieved:
Private Sub UserControl_Loaded( _
ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
'Library.CustomerEdit.BeginGetCustomer(123, AddressOf BindCustomer)
Library.CustomerEdit.BeginNewCustomer( _
Csla.DataPortal.ProxyModes.Auto, AddressOf BindCustomer)
End Sub
Remember from Part
2 that the BeginNewCustomer() factory creates a new object, while the BeginGetCustomer()
retrieves an existing object (or at least mocks that behavior). I've commented out
the BeginGetCustomer() call, so the code shown here is creating a new object.
Don't mind the ProxyModes.Auto parameter to BeginNewCustomer(). This is something
I'm working on to allow a factory method to force the data portal to run in local
mode, and will probably be the topic of a future blog post. The Auto setting indicates
that the data portal should use its default behavior.
In either case, the factory methods accept a delegate reference that points to a callback
handler. These factories are asynchronous, so the code you see here merely starts
the process. You need to implement a callback handler that is invoked when the
asynchronous operation is complete. That looks like this:
Private Sub BindCustomer( _
ByVal sender As Object, ByVal e As Csla.DataPortalResult(Of Library.CustomerEdit))
Me.LayoutRoot.DataContext = e.Object
End Sub
In C# you could shorten this code by using an anonymous delegate or multi-line lambda
instead of creating a totally separate method to handle the callback. Having tried
all three approaches, I must say that I still prefer the approach you see here, even
in C#, because I find it easier to read the code. Obviously you may find one of the
other approaches easier to read - personal preference at work.
The important thing to remember is that BindCustomer() is called by the data portal
when the asynchronous operation is complete. In other words, while the object was
being created/retrieved on the server, the user was still able to interact with the
UI and the browser. Then, when the server is done with its work, this BindCustomer()
method is invoked (much like an event being raised) so you can process the result.
The DataPortalResult parameter has a couple important properties: Object and Error.
The Object property contains a reference to the business object that was created/retrieved.
The Error property contains a reference to any exception that may have occurred during
the asynchronous operation.
Please note that the BindCustomer() callback occurs on the UI thread. Behind
the scenes the data portal is using WCF, and WCF callbacks in Silverlight always occur
on the UI thread. This is really nice, because it means that the developer doesn't
have to worry about marshalling the call to the UI thread like they would in WPF.
In the end, all this method does is to take the business object that was just returned
from the data portal, and uses it to set the DataContext for the top-level Grid control
in the UI. That causes all the detail controls contained in that Grid control to bind
to the object according to the binding expressions in the XAML.
Saving the Object
When the Save button is clicked, the object is either inserted or updated into the
database. As with CSLA .NET objects, CSLA Light objects keep track of whether they
are new or old, and so the UI developer doesn't need to worry about whether the save
is an insert or update - that's automatically handled by the data portal.
Also, like in CSLA .NET, the object keeps track of whether it has been changed. So
clicking the Save button when the object has not been changed will result in nothing
happening - the object automatically detects that it hasn't been changed and doesn't
really invoke the server to do a meaningless update operation.
In WPF, with CSLA .NET, the Save button probably would have no code, but would work
using the concept of commanding and the CslaDataProvider. Unfortunately commanding
doesn't exist in Silverlight, nor do data providers. So in Silverlight the Save button
behavior is implemented in old-fashioned code-behind event handlers.
The following code handles the Save button's Click event:
Private Sub Button_Click( _
ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim cust = CType(Me.LayoutRoot.DataContext, Library.CustomerEdit)
cust.BeginSave(AddressOf BindSaveResult)
End Sub
First, the business object is retrieved from the form's DataContext, and the object
is cast into the right type.
Then the BeginSave() method is called to start the save process. Again, all server
communication must be asynchronous in Silverlight and so the data portal is asynchronous.
This means the save operation is asynchronous as well, and so a callback delegate
is passed as a parameter.
Remember that the Click event handler exits immediately and the user can
interact with the UI and browser while the save operation is occurring on the server.
The UI will probably need to take steps to disable important buttons and controls
to prevent the user from interacting with the object (or flooding the server with
repeated save requests or something) while the asynchronous operation is executing.
That BindSaveResult() method will be invoked when the asynchronous operation is complete:
Private Sub BindSaveResult( _
ByVal sender As Object, ByVal e As Csla.Core.SavedEventArgs)
Me.LayoutRoot.DataContext = e.NewObject
End Sub
Just like in CSLA .NET, the data portal in CSLA Light returns a new object as
a result of the save operation. This means that the UI must be rebound to use
the new object when the operation is complete.
Deleting the Object
When the Delete button is clicked, the object is deleted from the database. CSLA Light
supports both immediate and deferred deletion. In this sample app I'm illustrating
deferred deletion, where the object has been retrieved, it is marked for deletion
and then saved. Here's the Click handler behind the button:
Private Sub Delete_Click( _
ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Dim cust = CType(Me.LayoutRoot.DataContext, Library.CustomerEdit)
cust.Delete()
cust.BeginSave(AddressOf BindSaveResult)
End Sub
The code is much like that for the Save button, but in this case the Delete() method
is called on the object before it is saved. This marks the object to be deleted, so
the data portal automatically routes the call to DataPortal_DeleteSelf() so that occurs.
The resulting object (which is now considered a "new" object) is bound to the UI in
the same BindSaveResult() method I discussed earlier.
Summary
In this post I have walked through the basic creation of a Silverlight UI that binds
to a business object. The object is created or retrieved through the data portal,
and then is inserted, updated or deleted through the data portal.
It is probably worth noting that CSLA .NET 3.6 will also include the same asynchronous
data portal concepts shown here, and so the same basic code can be written behind
a WPF, Windows Forms or even Web Forms UI.
Published Mon, 11 Aug 2008 17:50:41 GMT
by Rockford Lhotka
I've put another prerelease of CSLA Light (CSLA for Silverlight) and CSLA .NET 3.6
online at www.lhotka.net/cslalight/download.aspx.
-
Check out the new identity base classes in Csla.Security and Csla.Silverlight.Security
- they are pretty darn cool!
-
Check out the new Validator UI control - I think that is really cool!! It won't keep
that name, because we're merging the Authorizer functionality directly into that control,
so the result will be a consolidated control that does both things - we don't have
a name for it yet...
-
I'm pretty sure the ClientContext and GlobalContext work right, but the unit tests
don't show that - so I'd recommend viewing that functionality with suspicion.
-
I know some of the security tests don't work. Authentication is still evolving and
some of the test apps are lagging a bit behind.
Please remember, these are very early preview builds. They are not stable.
They are not complete. They may or may not work for you. They are not supported.
We have found, in testing, that Silverlight Beta 2 is not entirely stable. And that
WCF in Silverlight is particularly unstable. If you run the unit tests in
Silverlight you'll probably find what we find - that they randomly fail sometimes.
They'll run. They'll fail. Then they'll run again. With no discernable pattern. Quite
frustrating, but I guess this is the nature of working on a beta platform.
I have also put the sample app I've been blogging about at http://www.lhotka.net/files/cslalight/SimpleCslaLightAppvb-080803.zip.
This sample app shows all the things I've talked about in my Part 1 and Part 2 posts,
and I'll be coming out with more posts in the series in the next few days.
Published Mon, 04 Aug 2008 04:47:53 GMT
by Rockford Lhotka
CSLA .NET version 3.5.1 has been released and is now available at www.lhotka.net/cslanet/download.aspx.
This is a bug-fix release, and includes a number of very important bug fixes in the
following areas:
-
Windows Forms data binding
-
LINQ to CSLA
-
SmartDate type conversion
Please see the change
log for a full list of changes and fixes.
If you are using version 3.5.0, I strongly recommend upgrading to 3.5.1.
Published Mon, 04 Aug 2008 03:49:43 GMT
by Rockford Lhotka
The Twin Cities XNA User Group is hosting a very special event next month...
They're hosting a huge Halo 3 Tournament at Microsoft to raise money for Children's
Hospitals and Clinics of Minnesota. They will also have Rock Band 2 and a few
standalone machines for people to check out XNA Community Games.
Even if you aren't a gamer, you probably know one. You might even have one in your
family. This is an excellent cause worth donating to, and best of all: Magenic is
matching funds raised by the event (up to the first $3000)!!
For more information, and to register (or just donate), please visit http://www.charityfragathon.com
Published Fri, 01 Aug 2008 19:05:59 GMT
by Rockford Lhotka
In Part 1 I discussed
the high level architectural options offered by CSLA Light. In Part 2 I'll start walking
through the implementation of the 3-tier mobile object model (which pretty much also
covers the 4-tier mobile object model as well).
I'll start with the center of the application: the business layer. As I discussed
in Part 1, the
business layer spans Silverlight and .NET and defines mobile objects that are capable
of moving between the Silverlight client and the .NET web/app server.
(CSLA Light supports both C# and VB - and probably most other languages too - so the
fact that this particular demo is in VB shouldn't be taken as an indication that anything
only works with VB. Rather, I consciously switch between VB and C# for each demo I
write, and it is VB's turn.)
This is intended to be as simple a demo as I can create, and still show something
useful. To that end, the demo has exactly one object, with three properties.
Library.Client Setup
The Library.Client project is a Silverlight Class Library. This means it compiles
for the Silverlight runtime and can only contain code that will run on the Silverlight
client.
It is important to realize that when objects serialize and deserialize, they must
have the exact same type name. This includes the assembly name, namespace
and type name. The standard .NET way to represent an assembly qualified type name
is something like:
"MyNamespace.MyType, MyAssembly"
Since the business objects in Library.Client must be compatible with those in Library.Server,
both assemblies must compile to the same assembly name, and must use the same namespaces.
To accomplish this, the project's properties must be changed so the Assembly name
and Root namespace do not include the ".Client" part of the name:
I've changed them both to "Library", so this will compile to Library.dll and the root
namespace is Library.
Library.Server Setup
The same changes to the Assembly name and Root namespace are made to the .NET Class
Library project, Library.Server:
This project also builds a Library.dll with a root namespace of Library.
If you sign either project, you must sign both projects, and you must use
the same key. That way they end up with the same effective assembly name, even though
they are on different runtimes.
The end result is that a class in one project is serializable into and out of a class
in the other project.
CustomerEdit Class Declaration
The CustomerEdit class exists in both the Library.Client and Library.Server projects,
so the code is compiled into both runtimes. This means CustomerEdit can serialize
and move between the Silverlight and .NET runtimes.
The class declaration should be pretty familiar to any CSLA .NET developer:
Imports Csla
Imports Csla.Serialization
<Serializable()> _
Public Class CustomerEdit
Inherits BusinessBase(Of CustomerEdit)
The only odd or new thing here might be the inclusion of the Csla.Serialization namespace.
Silverlight has no concept of a Serializable attribute, so CSLA Light supplies one
(along with NonSerialized). So in CSLA .NET this namespace exists, but is essentially
empty, while in CSLA Light the namespace includes definitions for the attributes "missing"
from Silverlight. The end result is that a Serializable class in .NET can compile
in Silverlight.
Property Declarations
The property declarations are standard CSLA .NET 3.5 fare:
Private Shared IdProperty As PropertyInfo(Of Integer) = _
RegisterProperty(New PropertyInfo(Of Integer)("Id"))
''' <Summary>
''' Gets and sets the Id value.
''' </Summary>
Public Property Id() As Integer
Get
Return GetProperty(IdProperty)
End Get
Set(ByVal value As Integer)
SetProperty(IdProperty, value)
End Set
End Property
Private Shared NameProperty As PropertyInfo(Of String) = _
RegisterProperty(New PropertyInfo(Of String)("Name"))
''' <Summary>
''' Gets and sets the Name value.
''' </Summary>
Public Property Name() As String
Get
Return GetProperty(NameProperty)
End Get
Set(ByVal value As String)
SetProperty(NameProperty, value)
End Set
End Property
Private Shared StatusProperty As PropertyInfo(Of String) = _
RegisterProperty(New PropertyInfo(Of String)("Status"))
''' <Summary>
''' Gets and sets the Status value.
''' </Summary>
Public Property Status() As String
Get
Return GetProperty(StatusProperty)
End Get
Set(ByVal value As String)
SetProperty(StatusProperty, value)
End Set
End Property
This code compiles in both CSLA Light and CSLA .NET. Notice that I'm using managed
backing fields. This allows the CSLA Light serialization to automatically serialize
the field values to/from the .NET server.
You can use private backing fields if you choose. If you do use private backing fields,
then you'll need to override OnGetState() and OnSetState() and get/set each private
field value into the serialization stream. This process is much like the old PropertyBag
concept from VB6, and is necessary because Silverlight's support for reflection is
insufficient to automate the process.
By using managed backing fields, I avoid having to do that manual work, but I want
to be clear that you ultimately get to choose between the two approaches.
Silverlight vs .NET Code
The rest of this post will cover code that is either Silverlight-only or .NET-only.
In this sample I've chosen to use compiler directives to separate the code, even though
it is all in one file. You can certainly use partial classes instead, and based on this
forum thread that might be the preferred approach.
But in this sample, you'll find the following basic structure:
#If Silverlight Then
' Silverlight-only code
#Else
' .NET-only code
#End If
Compiler directives or partial classes work fine and achieve the same end result.
Silverlight Factory Methods
Now we're at a point where the Silverlight and .NET code may diverge. In Silverlight,
any server communication must be asynchronous. This means that the CSLA Light data
portal is asynchronous, and thus your factory methods must also be asynchronous.
We've added asynchronous capabilities to the CSLA .NET data portal as well, so you
can use this same async factory approach there as well, but typically I don't think
that makes a lot of sense.
In any case, the factory methods are designed to be called by the Silverlight UI,
and they look like this:
Public Shared Sub BeginNewCustomer( _
ByVal proxyMode As DataPortal.ProxyModes, _
ByVal callback As EventHandler(Of DataPortalResult(Of CustomerEdit)))
Dim dp As New DataPortal(Of CustomerEdit)(proxyMode)
AddHandler dp.CreateCompleted, callback
dp.BeginCreate()
End Sub
Public Shared Sub BeginGetCustomer( _
ByVal id As Integer, _
ByVal callback As EventHandler(Of DataPortalResult(Of CustomerEdit)))
Dim dp As New DataPortal(Of CustomerEdit)
AddHandler dp.FetchCompleted, callback
dp.BeginFetch(New SingleCriteria(Of CustomerEdit, Integer)(id))
End Sub
In both the create and fetch scenarios the steps are:
-
Create an instance of the data portal (optionally forcing it to run in local mode)
-
Add a handler for the async completed event
-
Call the data portal method to begin the async operation
For this example, I'm requiring that the UI developer pass in the event handler method.
In VB this is done with the AddressOf command, and in C# it is a delegate, an anonymous
delegate or a multi-line lambda. I'll get to the UI code in a future blog post.
When the data portal is in remote mode, it communicates with a web server running
a special CSLA .NET data portal host that is compatible with CSLA Light. I'll get
into the server configuration in a later blog post.
The result is that a BeginCreate() or BeginFetch() call results in the data portal
using WCF to call the server. When the call gets to the .NET server, it follows much
of my discussion in this
blog post. A few things have changed in terms of how a factory object is created,
but that's a topic for a different sample app.
In this sample app, the client-side data portal calls are routed directly to server-side
DataPortal_XYZ methods. These are normal CSLA .NET DataPortal_XYZ methods, and are
entirely unaffected by the fact that the client is Silverlight.
Silverlight Constructor
The one other bit of important code in the Silverlight-only area is a Public constructor:
Public Sub New()
' required by MobileFormatter
End Sub
The serializer used by CSLA Light is the MobileFormatter, and in Silverlight this
serializer requires a Public default constructor on any object it deserializes. This
is because Silverlight doesn't allow the use of reflection to create an object with
a non-Public constructor like you can in .NET.
This is unfortunate, because a UI developer might accidentally try to use the New
keyword to create an instance of the object, like:
Dim cust As New CustomerEdit
While this would compile, it won't really work - not as expected anyway. The UI developer
really needs to call one of the factory methods shown earlier to create or retrieve
the object successfully. This is why CSLA .NET constructors are typically non-Public,
to act as a reminder to the UI developer to use the factory methods. In Silverlight
you'll just have to use code reviews to ensure that the UI code is correct.
.NET Constructor
Now let's shift to the .NET-only code. In the .NET-only code there's also a definition
of a default constructor, but this one is Private:
Private Sub New()
' require use of factory methods
End Sub
This is standard CSLA .NET coding style, using a non-Public constructor to force the
UI developer to use the factory methods. In .NET the MobileFormatter does the same
thing as the BinaryFormatter or NetDataContractSerializer, and it creates an instance
of the object even though it has a non-Public constructor.
.NET DataPortal_XYZ Methods
To keep this sample as easy as possible, the DataPortal_XYZ methods don't actually
call a data access layer or talk to a database. They merely set a property on the
object to indicate which of the methods was invoked, and where it ran (on Silverlight
or on .NET):
Protected Overrides Sub DataPortal_Create()
LoadProperty(StatusProperty, _
"Created " & ApplicationContext.ExecutionLocation.ToString)
MyBase.DataPortal_Create()
End Sub
Private Overloads Sub DataPortal_Fetch( _
ByVal criteria As SingleCriteria(Of CustomerEdit,
Integer))
LoadProperty(StatusProperty, _
"Retrieved " & ApplicationContext.ExecutionLocation.ToString)
LoadProperty(IdProperty, criteria.Value)
LoadProperty(NameProperty, "Test " & criteria.Value)
End Sub
Protected Overrides Sub DataPortal_Insert()
LoadProperty(StatusProperty, _
"Inserted " & ApplicationContext.ExecutionLocation.ToString)
End Sub
Protected Overrides Sub DataPortal_Update()
LoadProperty(StatusProperty, _
"Updated " & ApplicationContext.ExecutionLocation.ToString)
End Sub
Protected Overrides Sub DataPortal_DeleteSelf()
LoadProperty(StatusProperty, _
"Deleted " & ApplicationContext.ExecutionLocation.ToString)
End Sub
There's nothing special about these methods. You could write this code in CSLA .NET
3.5 for a pure .NET application and it would work just fine. Sure, it is simplistic,
in that I'm just loading some hard-coded values into the object's properties, but
you should be able to imagine these methods calling your data access layer and getting
back a DataReader, DTO, entity object or whatever you are using; and then loading
the object's property values from that data source.
Summary
What I hope you take away from this post is that you can take an existing CSLA .NET
3.5 class (that uses managed backing fields), and get it running in CSLA Light too
with very little effort. In fact, all you need to do is:
-
Add an #If Silverlight compiler directive
-
Put the Silverlight-only factory methods and Public constructor in the Silverlight
area
-
Put your existing .NET-only factory methods and non-Public constructor in the .NET
area
-
Put your existing DataPortal_XYZ methods in the .NET area
-
Link the file into a Silverlight Class Library
In future posts I'll cover the Silverlight UI and the CSLA Light data portal configuration,
including the CSLA .NET data portal server.
Published Mon, 28 Jul 2008 23:47:05 GMT
by Rockford Lhotka
In preparation for the next public release of CSLA Light (CSLA .NET for Silverlight),
I'm going to do a series of posts describing the basic process of creating a CSLA
Light application. This first post will provide a high-level overview of the project
structure and some concepts.
First, it is important to realize that CSLA Light will support three primary physical
architectures: a 3-tier (or SOA) model, a 3-tier mobile objects model and a 4-tier
mobile objects model. At all times CSLA Light follows the same n-layer logical
architecture of CSLA .NET, where the application has the following layers:
-
Presentation/UI
-
Business
-
Data Access
-
Data Storage
The primary goal of CSLA Light and CSLA .NET is to support the creation of the Business
layer in such a way that the interface points to the Presentation/UI layer and to
the Data Access layer are clearly defined.
To a small degree, CSLA Light will assist in the creation of the Presentation/UI layer
by providing some custom Silverlight controls that solve common issues and reduce
UI code.
Like CSLA .NET, CSLA Light is not an ORM and does not really care how you
talk to your database. It simply defines a set of CRUD operators - locations where
you should invoke your Data Access layer to retrieve and update data.
3-Tier or SOA Model
If you use CSLA Light in a 3-tier (or SOA) model, your physical tiers are:
-
Silverlight
-
Service
-
Database
You can treat these as tiers, or as separate applications where you use SOA concepts
for communication between the applications. That's really up to you. In this case
the focus of CSLA Light is entirely on the Silverlight tier. In that Silverlight
tier, you'll have the following logical layers:
-
Presentation/UI
-
Business
-
Data Access/Service Facade
To do this, you configure the CSLA Light data portal to run in local mode and
it calls the DataPortal_XYZ methods right there in the Silverlight client tier. You
implement the DataPortal_XYZ methods to invoke remote services in the Service tier.
CSLA Light doesn't care what those services look like or how they are implemented.
They could be asmx, WCF, RSS, JSON, RESTful, SOAP-based - it just doesn't matter.
If you can call it from Silverlight, you can use it.
The CSLA Light data portal is designed to be asynchronous, because any calls to remote
services in Silverlight will be an asynchronous operation. In other words, the data
portal is designed to support you as you call these remote services asynchronously.
The only requirement is that by the time your async DataPortal_XYZ method completes,
that the object's data be created, retrieved, updated or deleted.
This is the simplest of the CSLA Light architectures (assuming the services and database
already exist) because you just create one project, a Silverlight Application, and
all your code goes into that project. Or perhaps you create two projects, a Silverlight
Application for the UI and a Silverlight Class Library for your business objects,
but even that is pretty straightforward.
3-Tier Mobile Objects Model
If you use CSLA Light in a 3-tier mobile objects model, your physical tiers are:
-
Silverlight
-
Data portal server
-
Database
This is more like a traditional CSLA .NET model, because the data portal is used to
communicate between the Business layer on the Silverlight tier and the Business layer
on the Data portal server tier. Your business objects literally move back and forth
between those two tiers - just like they do between the client and server tiers in
a CSLA .NET 3-tier model.
One interesting bit of information here though, is that your business objects are
not just moving between tiers, but they are moving between platforms. On
Silverlight they are CSLA Light objects in the Silverlight runtime, and on the Data
portal server they are CSLA .NET objects in the .NET runtime. The CSLA Light data
portal makes this possible, and the result is really very cool, because most of your
business object code is written one time and yet runs in both locations!
The Data Access layer only exists on the Data portal server, in .NET. So the Silverlight
tier relies on the data portal, and the .NET server, for all create, fetch, update
and delete behaviors. Also, that data access code is not even deployed to the client.
However, the client does have most business and validation and authorization logic.
So the user experience is rich, interactive and immediate - just like a WPF or Windows
Forms user experience.
This architecture is a little more complex, and requires that you create several projects.
The following figure shows a simple solution with the required projects:
Here's an explanation of the projects:
-
SimpleAppWeb: web app hosting the Silverlight xap file and the data portal service
-
Library.Client: Silverlight Class Library containing business classes
-
Library.Server: .NET Class Library containing business classes
-
SimpleApp: Silverlight Application containing the Silverlight UI application
The big thing to notice here, is that the CustomerEdit.vb class exists in both Library.Client
and Library.Server. In Library.Server it is a linked file, meaning that the
project merely links to the file that exists in Library.Client. You can tell this
by the different icon glyph in Solution Explorer.
The reason for this is that CustomerEdit.vb contains code for a business object that
moves between the Silverlight client and the .NET server. It contains code that compiles
in both environments.
You can then use compiler directives or partial classes to implement code in that
class that is Silverlight-only or .NET-only. I started a thread
on the forum about which is better - people seem to prefer partial classes, though
compiler directives is certainly a clean solution as well.
4-Tier Mobile Objects Model
If you use CSLA Light in a 4-tier mobile objects model, your physical tiers are:
-
Silverlight
-
Data portal server (web)
-
Data portal server (application)
-
Database
From a Silverlight perspective this is exactly the same as the 3-tier model.
The difference is purely in how the CSLA .NET data portal is configured on the web
server (#2). In this case, the CSLA .NET data portal is configured to be in remote
mode, so all data portal calls on machine 2 are relayed to machine 3.
This is totally normal CSLA .NET behavior, and doesn't change how anything else works.
But it can be a useful physical architecture in the case that the web server (#2)
that is exposed to the Internet so the Silverlight client can interact with it, is
not allowed to talk to the database. In other words, you could have an external firewall
between 1 and 2, and then an internal firewall between 2 and 3. So machine 2 is in
a DMZ, and machine 3 is the only one allowed to talk to the database.
What I'm describing is a pretty standard web deployment model for CSLA .NET - but
with a CSLA Light client out there, instead of a simple web page.
As I continue this series I'll dig into the code of this simple application, showing
how to create the business class, configure the data portal and set up the Silverlight
UI project.
Published Mon, 28 Jul 2008 16:26:08 GMT
by Rockford Lhotka
|
|
|
|
|
|
|
|
|
|
|
|