Quantcast
Channel: devforcecaliburn Wiki Rss Feed
Viewing all articles
Browse latest Browse all 20

Updated Wiki: Building a simple application

$
0
0

Building a simple application

In this section, we are going to develop a simple customer search screen using the framework. I personally like to approach things top down, so this is a way to see it all in action and then you can drill down further into the various parts of the framework, once you see it work together.

Prerequisites

Before we get started, let’s make sure we have all the pieces in place. The following list is what you need in order to follow along:

Getting Started

At this point I assume you have installed all the prerequisites and obtained and built the framework, hopefully using the preferred method of getting the latest version directly from source control using TurtoiseSVN or your favorite client.

Now, let’s fire up Visual Studio and get started. We are going to create a new project using the DevForce Silverlight Application template. This will put all the DevForce pieces in place.

image

After you click Ok, you should be presented with the following Solution containing the typical two projects for a Silverlight Application.

image

Next, we need to create a domain model. As we know, DevForce uses the ADO.NET Entity Framework for the data model. So, let’s right-click on the CustomerSearchWeb project and add an ADO.NET Entity Data Model. You can name it anything you like, but you should accept all the other defaults in the wizard to make things easier. We’ll use the NorthwindIB database and generate the model from the database. All we need for our simple application is the Customer table. Feel free to model the entire database if you’d like, though. Once completed you should have at a minimum the following structure in the .edmx file that got added to your web project. In addition, DevForce did its thing and generated code that will allow us to interact with this model.

image

 

Now we are ready to put the framework to use. First we’ll need to reference the framework assemblies. In the Silverlight project, add a reference to IB.Application.Framework.Core.SL, Caliburn.Micro.Extensions.SL as well as Caliburn.Micro. The former two assemblies can be found in the Bin folder under Silverlight at the root level of where you saved the source code. The Caliburn.Micro assembly can be found in the Libs folder at the same level. So, browse to each of the assemblies and add them as a reference. If everything was successful, you should see the references in your Silverlight project and at this point we should probably attempt a build, just to make sure everything is kosher so far.

image

With everything in place and hopefully you were able to actually build your solution, let’s do a quick refresher on DevForce. With DevForce, the client programming model revolves around what’s known as the EntityManager. The EntityManager is similar in function to the context in ADO.NET Entity Framework. It allows us to query our entities using LINQ, save any changes we made to entities and maintains a cache of entities and queries to avoid unnecessary round trips to the server and to handle offline scenarios. It also handles login, logout and authentication.

Instead of directly using an EntityManager by creating an instance, the framework follows the provider pattern to obtain a correctly configured EntityManager. Why we do this will become more obvious over time. But in short, to maintain flexibility, loose coupling and testability most of an application should not have to know what kind of EntityManager needs to be used. One may want to substitute the EntityManager, that connects to the real database, with an EntityManager that connects to the DevForce Fake Backing Store without having to change the application. It should simply be a decision during the bootstrapping of the application. Furthermore, once we get into design time data, one needs to be able to substitute the EntityManager with one that has sample data loaded into the cache. Again, we want to do this without having to change our views and view models etc. we simply want to inject a different EntityManager that pulls data from its cache instead of a database.

This feat is all accomplished by abstracting the EntityManager behind an EntityManager provider, that we can swap out whenever needed.

So, let’s create one of these EntityManager providers. Sounds like work, no? Well, not really. The heavy lifting is all done in the framework’s base implementation. The base implementation has a lot of pieces that will later help with authentication for example or cache coherence if you use multiple EntityManagers for things like sandbox editing and then need to sync the cache content between all these EntityManagers, but we are getting ahead of ourselves.

To create an EntityManager provider add a new class to your Silverlight project and name it EntityManagerProvider. For our simple application we are going to create one that returns an EntityManager, which connects directly to the NorthwindIB database that we used to create our domain model. Entity Framework has already conveniently put the connection string into our web.config file, so the EntityManager will have no issues connecting to the database.

After you added the class, modify the source code to look like this:

using IdeaBlade.Application.Framework.Core.Persistence;

namespace CustomerSearch
{
    public class EntityManagerProvider : BaseEntityManagerProvider<NorthwindIBEntities>
    {
        protected override NorthwindIBEntities CreateEntityManager()
        {
            return new NorthwindIBEntities();
        }
    }
}

In the framework’s spirit of low noise pollution, as we can see, implementing an EntityManager provider takes only a few lines of code. BaseEntityManagerProvider<T> is a generic class that works with the concrete EntityManager type that was generated when we created the domain model. NorthwindIBEntities is one of the classes that the DevForce code generator created for us. There are a couple of other special purpose BaseEntityManagerProvider<T> classes for when you want to use the DevForce Fake Backing Store or Design-time data.

Creating the view and view model

At this point, we have done the basic setup of our solution. We have our projects setup and created a domain model for our application. We also created an EntityManager provider that will allow us to obtain an EntityManager once we are ready to query some data.

Next, let’s start working at the other end of the tunnel. Every good application should be driven by the user experience. We are building a simple application here, so we are not going to go crazy on the user experience, but let’s figure out first what we actually want to show to the user. This will ultimately determine what data we need to retrieve and how to populate the UI.

In typical MVVM fashion there are two things we need to build. The view and the view model. The view is what the user will see on their display and the view model is what provides the data to be displayed and the actions that can be performed. Complete separation of concerns. Presentation is separate from the UI logic.

The DevForce Application Framework leverages Caliburn Micro for the MVVM part. If you are not familiar with Caliburn Micro, now may be a good time to squander over to the Caliburn Micro documentationhere and do a little reading.

We’ll start with creating the view. Naming the view is going to be important. One of the core concepts in Caliburn and Caliburn Micro is that the various parts get wired up together using naming conventions. That goes for how the view and view model get linked up as well as the XAML Binding. The out-of-the-box convention for pairing a view with a view model is if you have a view named MyApplication.View.MainView, the corresponding view model needs to be named MyApplication.ViewModel.MainViewModel. In other words, Caliburn drops the word “Model” everywhere from the full name of your view model class and then looks for a view matching the new name.

Ok, now that we’ve got the theory out of the way, let’s create the view. Remember we want to build a customer search screen, so lets design a view in Visual Studio like the following and name it MainView. Right-click on the Silverlight project and add a Silverlight User Control named MainView.xaml:

image

Using the Visual Studio Designer, create two grid rows and drop a TextBox and a Button in the first row and a DataGrid in the second row. Align the DataGrid so that it takes up the entire row. Using the designer ensures that the necessary assembly reference for the DataGrid gets added to our project.

image

Then modify the XAML like this:

<UserControl x:Class="CustomerSearch.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400""><Grid x:Name="LayoutRoot" Background="White"><Grid.RowDefinitions><RowDefinition Height="52*" /><RowDefinition Height="248*" /></Grid.RowDefinitions><TextBox Height="23" HorizontalAlignment="Left" Margin="21,12,0,0" 
                    x:Name="SearchText" VerticalAlignment="Top" Width="120" /><Button Content="Search" Height="23" HorizontalAlignment="Left" 
                   Margin="187,12,0,0" x:Name="Search" VerticalAlignment="Top" Width="75" /><sdk:DataGrid AutoGenerateColumns="true" Grid.Row="1" /></Grid></UserControl>

A couple of things are important to note here. Remember the naming convention stuff of Caliburn. The x:Name we are assigning to the TextBox and the Button are important. We will use these same names in the view model so Caliburn will auto-wire the binding for us. You’ll notice that the DataGrid doesn’t have an x:Name. That’s because the DataGrid is a control coming from the Silverlight 4 Toolkit. Caliburn doesn’t have an out-of-the-box convention for binding to a DataGrid. We are going to have to write the binding manually. Save everything and make sure your solution still builds. We are not going to win any user experience contests with this UI, but it’ll do for our purpose.

Next, we’ll have to back this view with some data and logic. That’s where the view model comes in. Right-click on the Silverlight project and add a new class. Remember naming is important. Based on what we named our view, we need to name this class MainViewModel. Once you have created the class, modify the source code like this:

using System.ComponentModel.Composition;
using Caliburn.Micro;

namespace CustomerSearch
{
    [Export]
    public class MainViewModel : Screen
    {
        private BindableCollection<Customer> _customers;
        private string _searchText;

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                _searchText = value;
                NotifyOfPropertyChange(() => SearchText);
                NotifyOfPropertyChange(() => CanSearch);
            }
        }

        public BindableCollection<Customer> Customers
        {
            get { return _customers; }
            set
            {
                _customers = value;
                NotifyOfPropertyChange(() => Customers);
            }
        }

        public bool CanSearch
        {
            get { return !string.IsNullOrWhiteSpace(SearchText); }
        }

        public void Search()
        {
        }
    }
}

Let’s talk about what’s happening here. First of all, we are extending the Screen base class we get from Caliburn Micro. This gives us a number of things, but most importantly, we get the all important INotifyPropertyChanged implementation to make all the XAML binding and refreshing work. The only thing I’d like to mention about this part is that Caliburn Micro has a very nice implementation of NotifyOfPropertyChange(), that takes a lambda expression instead of a magic string for the property name. This way the compiler will complain if you ever change the name of the property without changing the magic string. A common source of bugs in XAML.

What else? We’ve added a SearchText string property. Remember we named our TextBox in the view “SearchText”. Caliburn Micro will automatically bind the TextBox to this property based on the names, so that when we type in the TextBox, the content will go back and forward between the UI control and this property. We’ve also added a collection called Customers. We are searching for customers after all, so the search results have to be stored somewhere. This collection is what we are going to bind the DataGrid to. Customer is our customer object coming from the domain model we created earlier. We can directly expose DevForce entities to XAML. The entities themselves implement all the necessary behavior for the XAML binding to work. BindableCollection<T> is a Caliburn Micro subclass of ObservableCollection<T> that adds the Caliburn binding sauce to the mix.

So, before we forget it, let’s modify the DataGrid XAML to include the binding to the Customers collection. Modify the DataGrid line in the above XAML to look like this:

<sdk:DataGrid AutoGenerateColumns="true" Grid.Row="1" ItemSource="{Binding Customers}" />

There’s one more property and method that we added. The Search method is what gets executed as soon as we click the button. That’s where we later have to put some actual logic. Again, the wiring of the button’s OnClick event to this method is done based on the naming convention. The CanSearch property is what will get bound to the Enabled attribute of the button, controlling the graying out of the search button if nothing is in the search TextBox. Pretty nifty! All one has to do is create a property that prefixes the name of the UI control with “Can” and Caliburn will automatically bind it correctly.

Now the last piece is the little [Export] property that we decorated our class with. What is that all about? This is where the Managed Extensibility Framework (MEF), which underlies the DevForce Application Framework comes into play. By now you should have picked up that most of the required wiring to have all the various parts talk to each other is done behind the scenes. The XAML binding is mostly done using a naming convention. The pairing of the view with the view model is similarly done using a naming convention. The last piece that is missing is how do view models and for that matter other services and classes get found and wired up. The answer is through MEF discovery and dependency injection. To learn more about MEF, visit the link above as well as the section under “Using the Framework” in this documentation. So, what does the [Export] attribute do? It essentially registers this class with MEF, so it can be discovered at runtime. We’ll see in a little bit where that comes into play.

Bootstrapping the application

We’ve made quite a bit of progress with very little code written so far. That’s how it should be. After all we haven’t written a single line of logic that ultimately solves our business problem. Now there’s always a certain amount of setup that has to be done, so that’s where our coding efforts have gone so far, but I think the few lines of code that we had to write so far is pretty acceptable.

So, let’s see if we can finally run something here. Bootstrapping is the task of starting up an application and have it run through its initialization and throw the first screen onto the display, so we can start interacting with it. In the DevForce Application Framework this task is accomplished by a Boostrapper class. Right-click on the Silverlight project and add a new class. We’ll name the class AppBootstrapper, then modify the source code like this:

using Caliburn.Micro.Extensions;

namespace CustomerSearch
{
    public class AppBootstrapper : FrameworkBootstrapper<MainViewModel>
    {

    }
}

Well, that doesn’t look like much, but it’s all we need at this point. All the good stuff is implemented in the FrameworkBootstrapper<TRootModel> class. Here’s where we finally refer to the view model we created earlier. DevForce Application Framework will use MEF to locate the actual implementation of MainViewModel and create an instance and then activate it.

We are not quite done yet. The AppBootstrapper doesn’t just start on its own. The root object in every Silverlight application is the Application object and is typically found as App.xaml in your project. This is the first thing that gets created when the Silverlight application starts up. And that’s where we need to hook our Bootstrapper in.

First, the DevForce template we used to create our solution with, threw some stuff into the code behind of App.xaml that we no longer need. Right-click on the App.xaml in the Silverlight project and select “View Code”. Modify the source code like follows. You can also delete MainPage.xaml from the Silverlight project after you are done modifying the App.xaml.cs source code.

using System.Windows;

namespace CustomerSearch
{
    public partial class App : Application
    {

        public App()
        {
            InitializeComponent();
        }
    }
}

Now right-click on App.xaml again and select “View Designer”, then switch to the XAML view and modify the XAML like this:

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:app="clr-namespace:CustomerSearch" 
             x:Class="CustomerSearch.App"><Application.Resources><app:AppBootstrapper x:Key="AppBootstrapper" /></Application.Resources></Application>

We are simply adding the Bootstrapper as a static resource in order to have it created upon startup.

At this point, you can go ahead and press F5. If you followed the tutorial exactly, you will see the Silverlight application start up and present you with the view we have created earlier. It doesn’t look very pretty, but you can type in the TextBox and you should see the search button getting enabled. Clicking on the button, though, doesn’t do anything at this point and that is because the Search method in the view model has an empty body, but it seems like we have reached the point where we can implement some actual logic to make this application do what we want it to do.

Creating the data repository

A good application design and architecture uses appropriate abstraction layers and separation of concerns. In this vein, it is good practice to keep the view models free of any business or query logic. The purpose of the view model is to be the gateway between the view and the rest of the application. It should only have UI logic such as providing data to the view, handling actions and navigation to other views. It should not have to know the details of getting the data or updating the data. If one starts mixing business logic etc. into the view models, they become extremely tough to unit test and the business logic won’t be reusable at all as it would be completely tied into the UI.

To separate persistence logic from the view model, the DevForce Application Framework follows the repository pattern. The repository acts as an abstraction over the persistence layer and can easily be substituted with different implementations for unit testing of the view model for example. Therefore, it is a good practice to define an interface contract for the repository that can be implemented in different ways. Typically we create one repository per application module. The module’s repository is responsible for handling the entire object graph of data that the given module works with.

For our simple application we only have one module. So, let’s define the contract for the repository first. Right-click on the Silverlight project and add a new class (it’s actually going to be an interface). Name it IApplicationRepository and modify the source code like this:

using System;
using System.Collections.Generic;
using IdeaBlade.EntityModel;

namespace CustomerSearch
{
    public interface IApplicationRepository
    {
        INotifyCompleted SearchCustomers(string searchTerm,
                                         Action<IEnumerable<Customer>> onSuccess,
                                         Action<Exception> onFail);
    }
}

We are only going to do one thing in our application. We are going to search for customers. The repository contract defines just such a method for us. Notice how we are passing in two callbacks. This is going to be an asynchronous operation. The Silverlight application will have to make a request to the server if the data we are looking for isn’t already in the cache. Any request to the server is asynchronous in Silverlight. The results of the query will be passed to the OnSuccess callback. In case of an error, the exception will be passed to the onFail callback.

Now that we have our repository contract, let’s go back to the view model and complete the missing logic. Open MainViewModel.cs and modify the source code like this:

using System.ComponentModel.Composition;
using System.Windows;
using Caliburn.Micro;
using IdeaBlade.Core;

namespace CustomerSearch
{
    [Export]
    public class MainViewModel : Screen
    {
        private readonly IApplicationRepository _repository;
        private BindableCollection<Customer> _customers;
        private string _searchText;

        [ImportingConstructor]
        public MainViewModel(IApplicationRepository repository)
        {
            _repository = repository;
        }

        public string SearchText
        {
            get { return _searchText; }
            set
            {
                _searchText = value;
                NotifyOfPropertyChange(() => SearchText);
                NotifyOfPropertyChange(() => CanSearch);
            }
        }

        public BindableCollection<Customer> Customers
        {
            get { return _customers; }
            set
            {
                _customers = value;
                NotifyOfPropertyChange(() => Customers);
            }
        }

        public bool CanSearch
        {
            get { return !string.IsNullOrWhiteSpace(SearchText); }
        }

        public void Search()
        {
            _repository.SearchCustomers(SearchText,
                                        results => Customers = new BindableCollection<Customer>(results),
                                        e => MessageBox.Show(e.Message));
        }
    }
}

Let’s look at what changed. First off, we added a dependency to the repository interface. Clearly, we need the repository in order to perform the customer search. That was the whole point of creating the repository. We also added a constructor, that takes the repository as a parameter. This is known as dependency injection. Instead of the view model creating a new instance of the repository it is going to be injected from the outside. This approach will allow the repository to be substituted for unit testing of the view model for example. Then, we decorated the constructor with the [ImportingConstructor] attribute. This will instruct MEF at the time it creates an instance of MainViewModel to use this constructor and to locate a suitable implementation for IApplicationRepository and pass it to the constructor.

Lastly, we completed the implementation of our search method. Now that we have a repository, we simply call the SearchCustomers() method and in the respective callback, we either handle the result or the error. For handling the result, we create a new BindableCollection<Customer>, populate it with the results and then set the Customers property with the new collection. The XAML binding will kick in at that point and refresh the UI, because in the setter of Customers we are notifying the view that the Customers property has changed.

So far so good, but if you tried to run the application now, you would get an exception. The reason for that is the fact that we don’t have a suitable implementation for IApplicationRepository yet. So, let’s create one. Right-click on the Silverlight project and add a new class. Name it ApplicationRepository and modify the source code like this:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using IdeaBlade.Application.Framework.Core.Persistence;
using IdeaBlade.EntityModel;

namespace CustomerSearch
{
    [Export(typeof(IApplicationRepository))]
    public class ApplicationRepository : IApplicationRepository
    {
        private readonly IEntityManagerProvider<NorthwindIBEntities> _entityManagerProvider;

        [ImportingConstructor]
        public ApplicationRepository(IEntityManagerProvider<NorthwindIBEntities> entityManagerProvider)
        {
            _entityManagerProvider = entityManagerProvider;
        }

        private NorthwindIBEntities Manager { get { return _entityManagerProvider.Manager; } }

        public INotifyCompleted SearchCustomers(string searchTerm, Action<IEnumerable<Customer>> onSuccess,
                                                Action<Exception> onFail)
        {
            var query = Manager.Customers.Where(c => c.CompanyName.StartsWith(searchTerm));
            var op = query.ExecuteAsync();
            return op.OnComplete(onSuccess, onFail);
        }
    }
}

A few things worth mentioning here. First, since this is an implementation of our repository contract interface, we actually have to implement the interface. That’s pretty straight forward. What’s new here is that we are using a different version of the [Export] attribute. In this case we are registering this implementation with a specific contract type. As you remember from our view model above, we have a dependency on our repository contract interface and not the implementation of such. Essentially, we are registering our class here as an implementation of IApplicationRepository.

Similarly to the view model, we have an external dependency. Our external dependency here is the EntityManager provider. In order to run the search query, we need an EntityManager. The EntityManager provider itself has a contract interface, so we can swap out the actual implementation. The contract interface of an EnityManager provider is IEntityManagerProviderff<T>, where T is the type of the EntityManager.

Now, that we have all the pieces we can implement the actual search query. We are putting together a LINQ query that returns all customers, where the company name starts with the search term. Then, we are executing the query asynchronously, and once the query completes we handle the result or error. We are using an extension method that is part of the DevForce Application Framework to make the handling of the results a simple one line of code.

As you can see, we effectively hide the details of how we retrieve the data from the view model. We can change this implementation or substitute it with something that returns hardcoded data in order to unit test the view model, without needing the whole persistence layer and database.

Before we can run the application and see the fruits of our labor, we are missing one last piece. What we haven’t done so far is registering the EntityManager provider we created at the beginning of this tutorial. We do need to make an EntityManager provider available to the repository. Without it, we would still get an exception.

Right-click on the Silverlight project and add a new class. We’ll name the class EntityManagerProviderFactory, then modify the source code like this:

using System.ComponentModel.Composition;
using IdeaBlade.Application.Framework.Core.Persistence;

namespace CustomerSearch
{
    public class EntityManagerProviderFactory
    {
        [Export]
        public IEntityManagerProvider<NorthwindIBEntities> EntityManagerProvider
        {
            get { return new EntityManagerProvider(); }
        }
    }
}
}

Here we are using a third way of creating an export.  Instead of decorating a specific type with the [Export] attribute, whenever MEF needs an implementation of IEntityManagerProvider<NorthwindIBEntities> it will call the getter of this property to receive an instance. The reason we are doing it this way is because once you get further along, you’ll find yourself wanting to swap in different EntityManager providers. There are several ways to do that. One option is a compiler directive that swaps in a fake backing store provider for the Debug build and the real database provider for the Release build for example, or you may even have an option in the UI to switch between providers.

Now, we are done! If you followed along and didn’t make any mistakes you can run the application and search for customers. As a next step, consider adding a busy indicator to the view and throw it up in the view model before you make the call to the repository and when the asynchronous repository call completes you deactivate the busy indicator. This way the user gets some feedback and doesn’t click the search button multiple times if nothing seems to happen.

Summary

We are at the end of the tutorial. For a small application like this you may feel that the steps we went through may be overkill and you may be right. But for a real LOB application, that needs to remain flexible and maintainable, the design and architecture we laid down here is what will make it much easier to build the application, especially once you have multiple modules that you may want to load dynamically at runtime instead of one big XAP file that has to be downloaded all at once. If you look at it again, you’ll find that we wrote very little code outside of the core functionality and that is really the goal of the framework. You learned how the various parts get wired together by MEF instead of writing code that does the same. The goal is the same. Keep the housekeeping, plumbing and wiring code to a minimum. Feel free to experiment and submit suggestions for improving the framework if you wish.


Viewing all articles
Browse latest Browse all 20

Latest Images

Trending Articles





Latest Images