Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using the Microsoft Desktop Stack – Part 3: Using Entity Framework 4 in an MVVM Application

0.00/5 (No votes)
17 Apr 2011 2  
This article, shows how to integrate Entity Framework 4 into a WPF application using the MVVM pattern.

Introduction

Microsoft has revamped its desktop application stack in the past few years, moving from WinForms to WPF, from ADO.NET to the Entity Framework, and from the Jet database engine to SQL Server Compact Edition. This series of articles explains how to use the stack and presents checklists for implementing it.

The series consists of three articles:

Parts 1 and 2 contain checklists for setting up SQL Compact and Entity Framework for a desktop application. If you have been through those articles, you have seen the key components of the demo application.

This article, the third and final in the series, shows how to integrate Entity Framework 4 into a WPF application using the MVVM pattern. It presents an end-to-end solution for building a desktop application using WPF, Entity Framework 4 (EF 4), and SQL Server Compact Edition 4 (SQL Compact 4). The demo app, MsDesktopStackDemo, is built around the components and procedures introduced in Parts 1 and 2. It is a proof-of-concept application that was developed to work through the following issues:

  • How to create Repository classes that encapsulate Entity Framework 4 as completely as possible and allow the business and presentation layers of an application to remain ignorant of the ORM technology used.
  • How to create a specialized ObservableCollection class that can coordinate with the persistence layer, without being aware of the particular ORM technology used in that layer.
  • How to implement Entity Framework 4 as an ORM front-end to SQL Compact 4.
  • How to save binary large objects (BLOBs) to SQL Compact 4 as an image data type in order to circumvent the 8K size limitation of the varbinary type.

In addition, the article demonstrates my suggested approach to the MVVM pattern. The approach uses fully-coded ICommand classes designed to separate command code from the View Models that invoke those commands.

The demo app displays a partial list of books by Larry Niven and Jerry Pournelle. Some of these books were written jointly by the authors, and others were written separately. The cover art for the book is used to demonstrate how to use SQL Compact 4’s image data type to store BLOB objects. The demo app stores actual images to this data type, but it can be used to store any serialized BLOB, such as a serialized WPF FlowDocument.

This article assumes that the reader has a basic knowledge of WPF, Entity Framework 4, MVVM, and the Repository pattern. There are many articles, blog posts, and forum discussions on these topics available online.

Setting Up the Demo App

The demo app relies on a ‘private installation’ of SQL Compact 4.0. The SQL Compact binaries run over 5 MB, so I removed them before zipping the demo app for upload. To run the demo app, you will first need to copy the SQL Compact 4.0 binaries to the demo app’s Library folder. It shouldn’t take more than five minutes, assuming you have a global installation of SQL Compact 4.0 on your development machine. You can get complete step-by-step instructions on building a private installation in Part 1 of this series, or you can look in each Library subfolder for a text file with a list of files that need to be copied from the global installation.

Application Architecture

The application is designed around the MVVM and Repository patterns. The View communicates only with the View Model, which acts as an API for the application back-end. The View Model has access to the business model and to a services layer. The View Model is limited to a coordinator role—it delegates most of its work to a set of ICommand objects and to service methods.

The business layer of the application is made up of the business model and the ICommands and services referred to above. The business model is comprised of change-tracking objects created using Model-First development in Visual Studio 2010. The use of change-tracking objects creates some coupling between the business model and Entity Framework 4, which is discussed below. The View Model has access to all objects in the business layer. Commands have access to the business model and to services, and Services have access to the business model.

The persistence layer is made up of a set of Repositories, which encapsulate Entity Framework 4 and isolate it from the rest of the application. The repositories act as an API for data-store access in the application. Entity Framework 4 serves as the ORM technology for the application, although other ORMs, such as nHibernate, can be used. SQL Compact 4 serves as the data store; the Repository classes are set up so that the application can open and close SDF data files as needed.

Presentation Layer

The presentation layer consists of a single form—the main window of the application. The main window consists of XAML markup and code in the code-behind to display a message box as needed. Controls are data-bound to properties in the View Model. Data controls are bound to data properties, and command controls (buttons) are bound to command properties.

ViewModel

The View Model acts as a coordinator (some say a mediator) between the presentation layer and the rest of the application. The presentation layer knows only about the View Model and conducts all communication with the app back-end via the View Model. As a result, the View is shielded from changes in the back-end of the application, so long as the changes do not affect the interface of the View Model.

More importantly, the app back-end is shielded from changes to the View. The app is designed so that the View knows about its View Model, but the View Model knows nothing about its View. This approach offers increased flexibility in designing the View. It can be changed or completely replaced, and so long as it conforms to the interface of the View Model, it will work as before. If the View fails to conform to the View Model interface, the View won’t work, but that won’t affect any other parts of the program. In other words, we have isolated changes to the View and protected the rest of the program from those changes. That protection is a primary motivator for the MVVM pattern.

View Model Base: The View Model is derived from a ViewModelBase class, which implements two standard WPF interfaces:

  • INotifyPropertyChanged: This interface must be implemented by an MVVM View Model. The interface provides a PropertyChanged event, which enables post-change notification for WPF data binding, and for any other code that subscribes to the event.
  • INotifyPropertyChanging: This interface is not required by the MVVM pattern; it is provided as a convenience feature. It provides pre-change notification for any code that subscribes to the event, with the opportunity to cancel the change. The event can be used for object validation or for any other task that may need to be performed before a property is changed.

View Model properties must raise the PropertyChanged event if they are intended for use with WPF data binding. The properties generally include all data properties in the View Model. A property raises the event in its setter, like this:

public FsObservableCollection<Book> Books
{
    get { return p_Books; }

    set
    {
        base.RaisePropertyChangingEvent("Books");
        p_Books = value;
        base.RaisePropertyChangedEvent("Books");
    }
}

Simply raising the event is sufficient to enable WPF data binding on the property. If the View Model needs to respond to one of these events (for example, to perform object validation before a property change), then it must subscribe to the event, like this:

private void Initialize()
{
    ...

    // Subscribe to events
    this.PropertyChanging += OnPropertyChanging;
    this.PropertyChanged += OnPropertyChanged;
    
    ...
}

Commands: View Model command properties are instantiated to fully-coded ICommand objects. Some MVVM implementations use shortcuts to create pseudo-ICommands that link to methods in the View Model. In my opinion, this leads to code bloat in the View Model, and it can transform a View Model into the sort of monolithic controller that a distributed architecture tries to avoid.

In the demo app, command properties are instantiated by the View Model’s Initialize() method, which is called by its constructor. ICommand classes are located in the Commands folder of the source code.

Events: The View Model provides one event, UserMessagePosted. This event is used to publish a message for the end user from the application. The main window subscribes to this event; when it fires, the main window displays a message box with the content carried by the event args.

FsObservableCollection: EF 4 change-tracking works on individual objects, but not on collections. When I update the data in an object, EF 4 tracks the change and will update the data store to reflect the change. But EF 4 has no way of knowing when I add an object to an ObservableCollection, or when I delete an object from the collection. That means, I have to add any new object to the EF 4 object set, and I have to remove any deleted object from the EF 4 object set. The FsObservableCollection class is designed to facilitate these tasks.

The FsObservableCollection class derives from the WPF ObservableCollection class. It offers the following features, in addition to the standard features of the WPF ObservableCollection class:

  • Repository awareness: An FsObservableCollection takes an IRepository object in its constructor. As items are added to or removed from the collection, the class invokes IRepository methods to perform corresponding tasks on the data store, keeping the collection and the data store in sync.
  • Pre-change notification: The collection implements a CollectionChanging event, which provides pre-change notification when items are added to or removed from the collection. The event enables object validation, with an opportunity to cancel the change if validation fails.

The FsObservableCollection class is located in the ViewModel\BaseClasses folder. The objects supporting the CollectionChanging event are located in the ViewModel\Events folder.

Service classes: The View Model uses a service class, which is located in the ViewModel\Services folder. I make use of service classes wherever I can, to keep the actual View Model as lean as possible. Classes that serve only the View Model are located in the ViewModel\Services folder, while classes that serve both View Models and commands are in the Services folder at the project root.

Business Model

The business model for the demo app is very simple:

The Model was created in the Entity Data Modeler in Visual Studio 2010.

Change-tracking objects vs. POCOs: The demo app’s business objects were created as EF 4 change tracking objects. Proponents of Domain-Driven Design may prefer to use ‘POCOs’ (Plain Old C# Objects), on the grounds that the use of change-tracking couples the business model to Entity Framework 4. Those objections have some merit; I used change-tracking objects for a couple of reasons:

  • Entity Framework 4.0 does not provide built-in support for POCOs. An upgrade to Entity Framework that does provide POCO support is currently in CTP development, and will presumably be released as Entity Framework 4.1. The CTP supports POCOs through what is known as ‘Code-First’ development.
  • Change-tracking objects offer ease of use in return for the coupling to Entity Framework that they entail. Individual objects don’t have to be sent to a Repository every time they change; EF 4 tracks the changes automatically and updates the data store for all such changes whenever the data context is saved.

I tend not to be a DDD purist, and I am willing to accept some coupling between the business model and the ORM, assuming the coupling provides an adequate trade-off in terms of convenience. However, there is no universal rule on the subject, and I would recommend giving the ‘POCO vs. tracking object’ issue careful consideration during initial design of the application.

Commands

As I noted above, the View Model’s commands are fully-coded ICommand classes. I recommend this approach for several reasons:

  • It gets code out of the View Model, which reduces code bloat in the View Model and results in a more even distribution of functionality among collaborating objects, rather than a single, monolithic controller.
  • I find code in individual ICommand classes easier to understand than code in a large cluster of methods in a View Model.
  • Individual ICommand classes make it easier to implement Undo. Online discussions of the Command pattern explain how to implement undo in these classes.

Services

The demo app has one service class, FileServices, which is located in the Services folder at the project root. The FileServices class contains methods to get paths to the demo app’s data file and to the folder containing the PNG files used to load cover art into the database.

My production apps rely fairly heavily on these service classes. Generally speaking, if a piece of code is called by more than one command or View Model, it goes into a service class in the root-level Services folder. I find that approach make it easier to organize and find code later, and to avoid code duplication.

Repositories

The demo app Repository classes are located in the Persistence folder at the project root. The Repository classes are designed to completely isolate EF 4 from the rest of the application. Of course, total isolation would require the use of POCO objects, which the demo app breaches by using change-tracking objects. Nonetheless, the repositories are designed to fully encapsulate EF 4 and to require no knowledge of EF 4 in the business layer.

IRepository interface: The Persistence\Interfaces folder contains the IRepository<T> interface, which specifies a general contract for Repositories. The RepositoryBase<T> class implements this interface.

The RepositoryBase<T> class: Most of the work of the repositories is actually carried out by the RepositoryBase<T> class, which essentially wraps EF 4 functionality. The class is located in the Persistence\BaseClasses folder. RepositoryBase<T> has two constructors:

  • Owned object context: The first constructor takes a file path to a SQL Compact 4 (SDF) data file and the name of an Entity Data Model. If this constructor is used, the Repository creates its own EF 4 object context and disposes it when the Repository is disposed.
  • Shared object context: The second constructor takes an EF 4 object context, for those cases where the Repository will use a shared object context. When this constructor is used, the Repository does not dispose the object context when the Repository is disposed.

In the second instance, the persistence layer of the application will need a Repository factory class to hold the shared object context and provides it to new repositories as they are created. The demo app uses owned object contexts, so it does not require a Repository factory.

The RepositoryBase<T> constructor that we are using requires two arguments that come from EF4:

protected RepositoryBase(string filePath, Type contextType, string edmName)
{

At first, it looks like we have failed to isolate the rest of the application from EF4. Whoever calls this class will have to know about EF4. But what we are actually doing is delegating these tasks to concrete derived types. They are the only ones that can call the constructor. As you will see below, the EF4 information is hard-coded into a concrete repository. So we have preserved encapsulating EF4 within the repository, since the caller of the concrete class only needs to know a file path.

BookRepository class: The BookRepository class is the concrete class. It is located in the Persistence folder. A caller passes it a file path, and the BookRepository calls the RepositoryBase<T>, passing the file path and hard-coded values for the context type and the EDM Name.

public BookRepository(string filePath) : 
       base(filePath, typeof(BooksContainer), "Model.Books")
{
}

The BookRepository class does not need any functionality beyond what is provided by the base class, so the body of the class is left blank. RepositoryBase<T> does all the work. The base class exists simply to make that base constructor call. Obviously, it can also be used to hold any methods or properties that are unique to the concrete repository.

Note also that, unlike its base class, the BookRepository does not provide a ‘shared object context’ constructor. While the RepositoryBase<T> class is designed to be reused without modification in any application, the BookRepository class is designed as a concrete implementation and is dedicated to the demo app.

DataFileInfo class: The DataFileInfo class is not used by the application and is included simply to show how a lightweight object can be used in a production app to maintain basic information about the current data file. In a production app, the method that opens a data file would store the file path in this object, which can also be used to hold any other information needed about the state of the data file.

Logging

The demo app includes logging features, using the Open-Source log4Net logging framework. log4Net is configured for text file logging, and it uses a SpecialFolderPatternConverter class (found in the Utility folder) to place its logs in the c:\ProgramData folder. Configuration is done in the App.config file for the demo app.

Demo App Walkthrough

When you first run the demo app, it will open a window with no content. The demo app window has three buttons:

  • Load Books: This button loads a list of books from a SQL Compact 4 database. A list of titles will appear in the box on the upper left side of the app window. Click on a book in the list, and the names of the book’s authors will appear in the box on the lower left side. At the same time, the book’s cover art will appear in the box on the right side of the window.
  • Set Covers: This button copies the cover art for each book from PNG files to the SQL Compact 4 database. The covers have already been loaded, so the button is disabled. It is included to demonstrate how to store BLOB objects in a SQL Compact 4 database, and is discussed below.
  • Delete Book: This button is included to show how to perform data validation on a collection. When you click the button, the DeleteButtonCommand will attempt to delete the selected book. The View Model subscribes to the CollectionChanging event of the Books collection and handles the event in its OnBooksCollectionChanging() method. When the event fires, the event handler simulates an object validation failure. Then it sends an error message to the user and cancels the pending change.

Note that the data file for the demo app is stored in the Documents folder of the user who installed the demo. I don’t consider this an optimal approach—I used it because of limitations of Visual Studio deployment projects. This issue is discussed in Part 1 of this series.

App Initialization

The View and View Model are initialized by an OnStartup() override added to App.xaml.cs. The View is bound to the View Model by setting the View Model as the data context for the View:

protected override void OnStartup(StartupEventArgs e)
{
    base.OnStartup(e);

    var mainWindow = new MainWindow();
    var mainWindowViewModel = new MainWindowViewModel();
    mainWindow.DataContext = mainWindowViewModel;
    mainWindow.Show();
}

Loading Books

The Load Books button in the main window is data-bound in XAML to the LoadBooks property in the View Model. That property, in turn, is instantiated as a LoadBooksCommand object. When the button is clicked, it invokes the command, which loads books from the data store using the BookRepository class.

Once loaded, the book list is also bound in XAML to the SelectedBook property in the View Model via the book list’s SelectedItem property:

As a result, when a book is selected in the book list, it is also set as the current SelectedBook.

The View Model subscribes to its own PropertyChanged event in its Initialize() method. When a new SelectedBook is set, the OnPropertyChanged() event handler invokes a service method to load the book’s authors and cover art:

void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    switch(e.PropertyName)
    {
        case "SelectedBook":

            /* When we select a book, we need to configure the view model
             * to display the selected book's author(s) and cover art. */

            ViewModelServices.ConfigureSelectedBookProperties(this);
            break;

        ...
    }
}

The service method loads the newly-selected book’s authors to the list box in the lower left of the View, and it loads the cover art from the database to the Image control on the right. The code shows how to load a BLOB object from SQLCE using EF 4, and it is discussed below.

Setting Covers (Working with BLOBs)

The main window includes a Set Covers button that reads cover art for each book in the database from a PNG file and loads the image into an image column in the Books table of the database. The button is disabled, because covers only have to be set once, and that task has already been performed. The database comes pre-populated with cover art. If you want to execute the command, you can re-enable the Set Covers button by changing the return value of the command’s CanExecute() method from false to true.

The button is included to provide a demonstration of working with BLOB objects. BLOBs present a problem when working with SQL Compact 4 because its varbinary type has an 8K size limitation, which renders the type useless for storing most binary objects. Fortunately, SQL Compact 4 provides an image type that takes a byte array and can store up to 2^30-1 bytes. We can use the type with any BLOB that we can convert to a byte array.

The SetCoversCommand reads the cover art into a memory stream, converts that stream into a byte array, and assigns the byte array to the Book.Cover property:

// Load cover image for each book into its Book object
foreach (var book in bookList)
{
    var fileName = String.Format("{0}.png", book.Title);
    var coverFilePath = Path.Combine(coversFolderPath, fileName);
    var stream = new FileStream(coverFilePath, FileMode.Open);
    var byteArray = ConvertStreamToByteArray(stream);
    book.Cover = byteArray;
}

As is noted above, this technique can be used on any BLOB, not just on images. For example, a WPF FlowDocument can be serialized to a memory stream, at which point we can store it to an image column in exactly the same manner as the cover art in the demo app.

LoadBooksCommand invokes BooksRepository to load all books into the View Model’s Books property. The byte array representing the cover of each book is loaded into the Book.Cover property, which is an EF 4 Binary type. This type accepts a byte array, so we don’t have to do any conversion to load the cover art into the View Model.

The Image control in the main window is bound to the View Model’s CoverArt property, which is of type BitmapImage. That type works for the cover art we need to display. If we were working with a FlowDocument, we would use a View Model property of that type instead. In either event, we need to convert the byte array we have used for storage into the appropriate type for display. In the demo app, we need to convert the byte array into a BitmapImage.

Here’s how it works. As was noted above, selecting a book in the book list sets the SelectedBook property of the View Model. Changing the selected book fires the PropertyChanging event, which is handled by the View Model’s OnPropertyChanged() method. That method delegates its work to a View Model service class method, ConfigureSelectedBookProperties(). The service method performs the required conversion as it configures the View Model for the newly selected book:

public static void ConfigureSelectedBookProperties(MainWindowViewModel viewModel)
{
    ...

    // Show cover image for the newly-selected book
    using (var stream = new MemoryStream(viewModel.SelectedBook.Cover))
    {
        var coverArt = new BitmapImage();
        coverArt.BeginInit();
        coverArt.StreamSource = stream;
        coverArt.CacheOption = BitmapCacheOption.OnLoad;
        coverArt.EndInit();
        coverArt.Freeze();
        viewModel.CoverArt = coverArt;
    }
}

The code is pretty self-explanatory. It creates a new BitmapImage and streams the byte array into it. When the BitmapImage is complete, it assigns the object to the CoverArt property. If we were working with a different type of BLOB, such as a serialized FlowDocument, we would convert the byte array back to a memory stream and deserialize that stream to the appropriate object.

This technique allows SQL Compact 4 to store BLOBs of almost any type. However, as you can see from the demo app database, BLOBs can quickly bloat a SQL Compact database to an unmanageable size. So, the technique should be used judiciously.

Deleting Books

As was noted above, the Delete Books button is provided to demonstrate how to use the CollectionChanging event in the FsObservableCollection class to perform object validation. The OnBooksCollectionChanging() method handles the event by simulating a validation failure. It demonstrates how to notify the user and cancel the pending deletion.

Command-based validation: Note that the CollectionChanging event is not strictly required to perform object validation. In MVVM architecture, a command object can perform object validation before invoking an operation that will change an object or a collection. One of the benefits of fully-coded ICommand classes is that, if you choose to validate from your ICommand objects, these validation methods do not clutter the View Model.

Event-based validation: The demo app shows another approach to object validation, known as event-based validation. The DeleteBookCommand invokes the Remove() method on the Books property in the View Model. Note that it does not perform any validation before invoking the method. The Books property is an FsObservableCollection, so it raises a CollectionChanging event before making the deletion. The View Model subscribes to this event in its Initialize() method:

private void Initialize()
{
    ...

    // Subscribe to events
    this.PropertyChanging += OnPropertyChanging;
    this.PropertyChanged += OnPropertyChanged;
    this.Books.CollectionChanging += OnBooksCollectionChanging;
}

The subscription delegates event handling to the OnBooksCollectionChanging() method.

The event handler is a simple dispatcher that invokes a service class method to ‘post’ a message to the user (see below on posting user messages). Once the service method has posted the user message, the event handler cancels the pending deletion by setting the event args’ Cancel property to true.

void OnBooksCollectionChanging(object sender, NotifyCollectionChangingEventArgs<book /> e)
{
    ViewModelServices.PostCancelMessage(this);
    e.Cancel = true;
}

The event handler returns control to the FsObservableCollection.Remove() method, which checks the Cancel property. Finding the property set to true, the method exits without executing the deletion.

I don’t have a strong preference between command-based object validation and event-based validation. The CollectionChanging event is provided to give the developer the option of using event-based validation if they choose to do so.

Subscribing to the CollectionChanging event: As long as the collection itself doesn’t change, a View Model need only subscribe to the event when it is initialized. However, note that the demo app shows an empty book list on startup. To do that, we need to initialize the View Model’s Books property to an empty collection. So, we call the collection constructor that takes only a Repository argument, passing null to indicate that we don’t have a repository yet:

private void Initialize()
{
    ...

    // Initialize data properties
    this.Books = new FsObservableCollection<Book>(null);

    // Subscribe to events
    ...
    this.Books.CollectionChanging += OnBooksCollectionChanging;
}

The View Model subscribes to the CollectionChanging event of this collection.

When the user clicks on the Load Books button, LoadBooksCommand creates a BooksRepository, uses it to get a book list, and passes both the list and the repository to a new FsObservableCollection. Then it sets its Books property to the new collection. Note that when it does so, it completely replaces the empty collection that we originally subscribed to. That means we need to unsubscribe from the old collection before it is replaced, and we need to subscribe to the new collection after the replacement is made.

The View Model uses its PropertyChanging and PropertyChanged events to perform these tasks. When the Books collection is replaced, the PropertyChanging event is fired before the event is executed. The OnPropertyChanging() event handler unsubscribes from the old collection:

void OnPropertyChanging(object sender, System.ComponentModel.PropertyChangingEventArgs e)
{
    switch (e.PropertyName)
    {
        case "Books":
            this.Books.CollectionChanging -= OnBooksCollectionChanging;
            break;
    }
}

Then, after the change is executed, the PropertyChanged event is fired. The OnPropertyChanged() event handler subscribes to the new collection:

void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    switch(e.PropertyName)
    {
        ...

        case "Books":
            this.Books.CollectionChanging += OnBooksCollectionChanging;
            break;
    }
}

Note that the unsubscribe step is important. If you fail to unsubscribe from your objects’ events when you are finished with them, .NET garbage collection may fail on those objects, eventually crashing your application or seriously degrading performance.

Posting the user message: As was noted above, the OnBooksCollectionChanging() event handler invokes a service class method, PostCancelMessage(), to create the message that is displayed to the user when the Delete Book button is pressed. The service class method configures the data for the UserMessagePosted event args and passes them back to the View Model’s PostUserMessage() method. This method creates event args and raises the event.

The presentation layer subscribes to this event and uses the event args to create a message box, which it displays for the user. The main window subscribes to the View Model’s UserMessagePostedEvent when its data context is set. It uses a two-step process in its code-behind class to subscribe to the event.

First, the main window’s Initialize() method (which is called from its constructor) subscribes to its own DataContextChanged event:

private void Initialize()
{
    // Subscribe to local events 
    this.DataContextChanged += OnDataContextChanged;
}

Next, the handler for that event, OnDataContextChanged(), subscribes to the UserMessagePosted event:

void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    // Subscribe to view model events
    var viewModel = (MainWindowViewModel) this.DataContext;
    viewModel.UserMessagePosted += OnUserMessagePosted;
}

The event handler gets the View Model by casting its data context to the appropriate type. Then it subscribes to the event. As a result, when the OnStartup() override (in App.xaml.cs) sets the main window’s data context, the window automatically subscribes to the UserMessagePosted event.

MVVM purists might object to the use of code-behind in the main window. A strict MVVM approach would dictate that code-behind never be used. While there may be merit in that approach, I am comfortable using some minimal code-behind to implement legitimate concerns of the View.

The objective of the MVVM pattern is to provide a clean separation of concerns between the presentation layer and the app back-end. From that perspective:

  • The View Model is concerned with the content of the user message; and
  • The presentation layer is concerned with the display of the message

That means that the content of the message must be generated by the View Model and passed to the View for display. As was discussed above, the demo app implementation of MVVM is designed so that the View Model knows nothing of its View. Accordingly, an event (which does not require the publisher to know anything about subscribers) provides an appropriate vehicle for transmitting the content without compromising that separation between the View and the View Model.

In a production app, one could expect that several different Views would need to respond to UserMessagePosted events raised by their View Models. Accordingly, the Views respond to these events, but they delegate the generation of the message to a service class in the presentation layer.

The Deployment Project

The demo app solution also includes a deployment project that can be used to install the demo app on end-user machines. The deployment project is included as an example of how to create a desktop installer for apps that use the Microsoft Desktop Stack. The installer includes the ‘unitary database’ installation features discussed in Part 1 of this series.

Conclusion

Hopefully, you have enough information to build a production application along the same lines. The design presented in this article will suffice for relatively simple applications, but one finds that even a well-structured MVVM app can become bloated and brittle as the application grows in complexity. For that reason, effective application partitioning, and the use of an Inversion of Control (IOC) container, are essential elements of most production applications.

Microsoft Prism 4.0 provides a framework for partitioning an app into independent components that can be composed into one or more windows for presentation to the user. Prism works with most popular IOC containers, including the .NET Extensibility Framework, Microsoft Unity 2.0, Castle Windsor, and others. I would suggest incorporating these technologies into all but the most basic production apps.

As always, I welcome your comments and suggestions for improving this series. I find that the peer review provided by CodeProject readers is invaluable, and it is always appreciated.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here