Vibrant discussion about CSLA .NET and using the framework to build great business applications.
I'm quite familiar with CSLA.NET for WinForms and WPF but I'm struggling to make the mental leap to CSLA for Silverlight in a couple of areas.
I tend not to use the CslaDataProvider. I like to instantiate business objects myself and control their lifetime, usually in a VIewModel. What I'm struggling with is how to write the initialization code in the VM that creates and / or loads business objects. Given the asynchronous nature of Silverlight and the CSLA DataPortal, how would one write code that instantiates an object but performs some other work. If the call to the Create_____ factory method is async, how do I wait for that call to return before continuing? I've looked at some examples that talk about blocking the non-UI thread but this seems like a lot of code just to instantiate an object. Is that really how it has to work? My apologies if this seems a bit vague but after reading far too many blog posts and articles I'm really not sure what is the right way.
Regards,Dave
This is the reason for the ViewModelBase and ViewModel classes - to help manage these details.
The data provider model's strength was in defining a communication model that worked in sync and async scenarios - including reporting errors and providing the resulting data when it was available.
The ViewModel base class does this too. You are still required to create (or at least trigger the creation) of the business object, but the base class takes care of updating the Model property when the object actually arrives on the client, or the Error property in the case of an async error.
Of course you can implement other properties and verb methods on the viewmodel class - but the basic required functionality for a typical business object is already there with these base classes.
For example, here's a viewmodel that supports a simple edit screen (create an object, let the user edit the properties, save the object):
(this is CSLA 4 - but the 3.8 code would be extremely comparable)
using System;using Csla.Xaml;using Bxf;namespace Collective{ public class PostEditViewModel : ViewModel<Library.PostEdit> { public PostEditViewModel() { Presenter.Instance.ShowStatus(new Status { IsBusy = true, Text = "Creating post" }); BeginRefresh("NewPostEdit"); } protected override void OnRefreshed() { base.OnRefreshed(); Presenter.Instance.ShowStatus(new Status()); } protected override void OnError(Exception error) { base.OnError(error); Presenter.Instance.ShowError(error.ToString(), "Search data error"); Presenter.Instance.ShowStatus(new Status()); } public override void Save(object sender, ExecuteEventArgs e) { Presenter.Instance.ShowStatus(new Status { IsBusy = true, Text = "Saving..." }); base.Save(sender, e); } protected override void OnSaved() { Presenter.Instance.ShowStatus(new Status { IsOk = true, Text = "Post saved" }); base.OnSaved(); } public void Close() { Presenter.Instance.ShowView(null, null, null, "Content"); } }}
The Bxf namespace and Presenter construct are from a small (Basic Xaml Framework: Bxf) UI framework I'm playing with. But that's secondary - the important thing for your question is to understand how the viewmodel class works.
The constructor(s) call BeginRefresh(), passing in the name (and parameters) of the static factory method - which is async of course. That starts the process of creating or fetching the business object.
If the create/fetch works, the Model property is set, which automatically updates the UI via binding (the UI binds to the Model property). In my case I'm overriding OnRefreshed() to do some status bar update work.
If the create/fetch/save fails, the Error property is set, which might automatically update some UI element. In my case the OnError() override is triggering the UI framework to display the error and update the status bar.
The base class already has a Save() method that does the right thing. I'm overriding Save() just to do status bar updates, and the same with overriding OnSaved().
The Close() method is a verb - invoked through the TriggerAction (like InvokeMethod or Execute) component when the user makes the appropriate UI gesture to close this form. In my UI this is a button click, but it could be any appropriate UI event.
Rocky
Rocky,
One thing I'm still hung up on is unit testing a CSLA-based business object for Silverlight. Can you point me to some example code in the downloads? Specifically I'm trying to understand how to deal with creating a new BO using the async factory methods. Perhaps this is not strictly a CSLA question. I'm playing around with ManualResetEvent but I think I'm missing something.
If I wanted to test inserting an object, I have to create one first. Here's my test code:
private static ManualResetEvent waitHandle;
public static Role CreateObject()
{
waitHandle = new ManualResetEvent(false);
waitHandle.Reset();
Role obj = null;
Role.NewRole((o, e) =>
obj = e.Object;
obj.Name = "Name";
obj.Description = "Description";
waitHandle.Set();
});
bool result = waitHandle.WaitOne(5000);
if (result)
return obj;
else
throw new Exception("CreateObject timed out.");
}
[TestMethod]
public void Insert()
UnitTestContext context = GetContext();
var obj = CreateObject();
obj.BeginSave((o, e) =>
var savedObject = (Role)e.NewObject;
context.Assert.IsNull(e.Error);
context.Assert.IsNotNull(savedObject);
context.Assert.IsFalse(savedObject.IsNew);
context.Assert.IsFalse(savedObject.IsDirty);
context.Assert.IsFalse(savedObject.IsDeleted);
context.Assert.Success();
The problem is that the waitHandle times out.
Now, my test code may not be correct yet and I haven't introduced any mocking and such yet. I'm just trying a proof-of-concept of sorts to see if I can get a single object to work from UI to DB and back and all of the layers in between.
Thanks,Dave
I'm going to post this as a new question.