ActiveRecord, Caliburn Micro and one line of code

For some months ago I started a WPF project for my employer. The application that is meant to be the result of the project will include simple record viewing and editing.

After some research, I choose to use Castle ActiveRecord for database interaction and Caliburn Micro’s implementation of the MVVM (Model-View-ViewModel) pattern. As far as the record viewing and editing part is concerned, this turned out to be a lucky choice. Besides from building the Model and drawing a View in design mode, the ViewModel has exactly one line of code!

Caliburn Micro’s MVVM implementation is built on a “Model first” principle, which basically means that you let your code load a ViewModel and the framework will load the related view. When nothing else is specified, “MyFirstViewModel” will search for and load “MyFirstView”. Of course you can connect your ViewModel to more, or another View – but that’s not interesting for this article.

Let’s take the person registration as example. As simple form where you enter a name, address, phone, etc.  First, we create the Model and apply the ActiveRecord attributes to it:

[ActiveRecord]
public class Person
    : ActiveRecordBase<Person>
{
    [PrimaryKey]
    public int PersonId { get; set; }

    [Property]
    public string FirstName { get; set; }

    [Property]
    public string LastName { get; set; }

    // add more properties here ...
}

As you can see, the Person object only contains properties, no methods. The ActiveRecordBase base class contains methods for database CRUD operations and for this object, no additional methods are required.

Next, we’ll build the view in XAML. For the sake of simplicity, styles and other GUI related expressions have been omitted from the code below. Notice the link to Caliburn Micro in the header of the UserControl.

<UserControl x:Class="PersonView"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:cal="http://www.caliburnproject.org">
    <Grid Background="Transparent" DataContext="{Binding Path=Person}">
        <StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="Firstname" />
                <TextBox Text="{Binding FirstName}" />
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="Lastname" />
                <TextBox Text="{Binding LastName}" />
            </StackPanel>
        </StackPanel>
       
        <Button Content="Save" cal:Message.Attach="Save()" />
    </Grid>
</UserControl>

Last – but not least – the ViewModel. Now, the code in the ViewModel is actually what this article is about. Not because it’s huge or complex – rather because it’s almost too simple. See here:

using Caliburn.Micro;

public class PersonViewModel : Screen
{
    public Person Person { get; set; }
}

That’s all folks!

So, how can you actually create or edit records here that’ll be persisted in the database? The trick is the intelligence of Caliburn Micro with the base method of ActiveRecord.

In part 3 of the “Soup to Nuts” series of articles that Rob Eisenberg wrote about Caliburn Micro1, he explains how the button’s default event (the Click event) is used when no other event is specified. That’s how the Save() method is called when the Button at the bottom of the XAML code is clicked.

Finally, there’s this little footnote in the same article that does the trick:

Actually, if no handler is found, before an exception is thrown, the framework will check the current DataContext to see if it has the requested method.

So, when my ViewModel doesn’t have any Save() method specified, Caliburn Micro will check the DataContext (read: the Person object instance) for this method. Now, the Person object doesn’t have this method either, but its base method – ActiveRecordBase – does! And that’s why I don’t need to write more than one line of code in my ViewModel: the only thing I need to tell the View is that its DataContext is a Person object.

Another thing with Caliburn Micro is that it instantiates a new object instance of the DataContext whenever the UserControl (or Window) is opened and the DataContext isn’t assigned. So, when creating a new Person, I don’t have to instantiate it in my code. Again, that leaves less code for me to write. The only thing that’s left is to find the right Person when you need to edit.

public void EditPerson(int personId)
{
    Person = Person.FindOne(Restrictions.Eq("PersonId", personId));
}

Building your application won’t get much simpler, will it?

Note:
In this article, I’ve tried to explain a concept, which not necessarily means that the article in its whole or any part of it – including the code samples – will give a ready-to-run application when applied. The purpose of this article is to understand how the concept works and how you could apply this concept in your project. I’m aware that the sample code is fairly limited, but in my opinion this can easily be extended once you understand the concept.

0 comments:

Post a Comment