Alessandro Del Sole's Blog

/* A programming space about Microsoft® .NET® */
posts - 159, comments - 0, trackbacks - 0

My Links

News

Your host

This is me! This space is about Microsoft® .NET® and Microsoft® Visual Basic development. Enjoy! :-)

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.

Microsoft MVP

My MVP Profile

I'm a VB!

Watch my interview in Seattle

My new book on VB 2015!

Pre-order VB 2015 Unleashed Pre-order my new book "Visual Basic 2015 Unleashed". Click for more info!

My new book on LightSwitch!

Visual Studio LightSwitch Unleashed My book "Visual Studio LightSwitch Unleashed" is available. Click the cover!

Your visits

Follow me on Twitter!

CodePlex download Download my open-source projects from CodePlex!

Article Categories

Archives

Post Categories

.NET Framework

Blogroll

Help Authoring

Microsoft & MSDN

Setup & Deployment

Visual Basic 2005/2008/2010

WPF: Introducing the Model-View-ViewModel pattern for Visual Basic 2010 developers - part 5

After a quite long interval since my last post on the Model-View-ViewModel pattern with WPF 4 and Visual Basic 2010, let's retake the discussion starting from my promise about discussing such a pattern in a more interesting scenario: the ADO.NET Entity Framework. Before I show you some code, I need to do some explanations.

Where do we want to go?

At the end of the blog post series we will have the following application up and running:

It is a very simple master-details application, that will also allow launching a new dialog window. With no doubt really and really simple. But it's simple if you think about it in a non-MVVM fashion, thus considering mouse clicks and event handlers instead of command bindings, data binding and ViewModels. In fact you will understand how complex the MVVM pattern is but also why it offers some great benefits. We will talk about message brokers, service locators and other stuff. My objective here is not necessarily showing a real world application: my objective is providing the easiest way for understanding complex concepts, how MVVM works, what is the level of abstraction to be reached. I needed to tell you this.

I'm not a perfect man

According to what I said in the last sentence, I can make mistakes (although I hope this won't happen). Again, I'm not an MVVM guru; I'm simply sharing with you what is the path I'm following while studying the pattern. Because of this, things could not be perfect so I do really appreciate any feedbacks or suggestions from developers that know MVVM better.

How many ways to do it?

Previously I told you that MVVM is a pattern, not a rule. It's a set of guidelines with a clear goal. By the way, there are too many different implementations and this can lead to confusion. The real story is that I was starting the series on MVVM against Entity Framework but I understood that things were not so easy and further studies made me understand the complex work to do in order to reach the maximum level of abstraction. I was helped by some friends MVPs and I was pointed to the right way. Think about this scenario: a ViewModel directly interacts against the DAL; certainly it works but it's not "clean". I found some examples on the Internet that showed things with this approach or other examples showing things the most complex way but not so well fitting, in my humble opinion. I followed some cool indications from MVPs and I restarted my work. Again, the code you will see in this series could not be perfect but everything can be improved and I will if necessary. Let's stop chatting, it's time to write code.

Creating the sample project

After launching Microsoft Visual Studio 2010, let's create a new WPF 4 project in Visual Basic 2010. In this first post I will not actually discuss MVVM, instead the goal is preparing the basis for next articles. Within Solution Explorer let's add the following subfolders to the project: Models (that will store the data), ViewModels (that will store all ViewModels), Views (that will store all views), Commands (that will store the commands logic implementation), Helpers (that will store support classes) and Services (that will store service interfaces and classes that I will explain later).

The Entity Data Model a.k.a. our Model

ADO.NET Entity Framework is a complex data access technology. I don't want to make a discussion on "domain models" and so on, we will simply utilize the Entity Framework as the data access layer against the Northwind database installed on SQL Server. Using the Entity Framework we are going to map as .NET objects the following tables: Customers, Orders and Order Details. With that said, right click the project name in Solution Explorer -> Add|New Item -> Data tab and ADO.NET Entity Data Model template. Assign the Northwind.edmx file name to the EDM and proceed through the wizard steps by selecting the Northwind database and leaving unchanged the default settings, ensuring that required tables are selected like in the following figure: 

At the end of the Wizard, the Entity Framework designer will show in Visual Studio 2010 the graphical representation of the table/columns mapping as shown here:

At this point we have some classes that represent our data and that for this reason can be considered as our Model

Considerations about Entities as Model

As I said before, Entities are .NET classes and thus can be considered as our Model. By default entities implement the INotifyPropertyChanged and this is one more reason to consider them as the Model. In my previous posts this was not necessary because we were working against a collection of objects, but you could bind a single instance of a business object to the UI and so having implemented such an interface is absolutely useful. Moreover, previously we implemented that interface in the ViewModel so that it could send notificatinons to the View. But it's not a bad thing if it is also implemented by the model, providing a notification point to external objects such as ViewModels. The reason why entities implement INotifyPropertyChanged is that they need to send notifications to the caller, but this is another story.

Implementing data validation

In this post I will not talk about the ViewModel. I will instead discuss something that previously I showed in the last post: implementing data validation rules. If you take a look at the first figure in this blog post, you can notice how the application provides the ability of managing orders for each customer. Let's imagine that an order, new or existing, will be accepted only if the ship country is entered. In the code, this is represented by the ShipCountry property from the Order class. If you are not absolutely new to the Entity Framework, probably you know that Visual Studio generates two useful methods: OnXXXChanging and OnXXXChanged where XXX is the name of the property that you want to validate. In our case we have OnShipCountryChanging and OnShipCountryChanged although we are just interested into the latter, that is invoked when the property gets modified. Since a good programming rule is that you do not change autogenerated files, we can take advantage of two important features in Visual Basic: partial classes and partial methods. In other words, we create a partial Order class that extends the autogenerated one. With that said, within the Models project subfolder let's add a new class and call the code file as Order.vb. In this class:

  1. The definition header is Partial Class

  2. we will implement the IDataErrorInfo interface for data validation

  3. we will implement the validation logic

  4. within the OnShipCountryChanged method body we will add or remove an error to the validation errors collection, depending on the entered value.

This is the code for the partial class, with comments:

Imports System.ComponentModel

 

Partial Public Class Order

    Implements IDataErrorInfo

 

    'Collection of error/description

    Private m_validationErrors As New Dictionary(Of String, String)

 

    'Adds an error to the collection if not already present with the same key

    Private Sub AddError(ByVal columnName As String, ByVal msg As String)

        If Not m_validationErrors.ContainsKey(columnName) Then

            m_validationErrors.Add(columnName, msg)

        End If

    End Sub

 

    'Removes an error from the collection, if existing

    Private Sub RemoveError(ByVal columnName As String)

        If m_validationErrors.ContainsKey(columnName) Then

            m_validationErrors.Remove(columnName)

        End If

    End Sub

 

    'Extends the class with a property that is True if the instance has validation errors

    Public ReadOnly Property HasErrors() As Boolean

        Get

            Return m_validationErrors.Count > 0

        End Get

    End Property

 

    'Returns an error message

    'In this case it's a generic message

    Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error

        Get

            If m_validationErrors.Count > 0 Then

                Return "Order data is invalid"

            Else

                Return Nothing

            End If

        End Get

    End Property

 

 

    Default Public ReadOnly Property Item(ByVal columnName As String) As String _

                                     Implements System.ComponentModel.IDataErrorInfo.Item

        Get

            If m_validationErrors.ContainsKey(columnName) Then

                Return m_validationErrors(columnName).ToString

            Else

                Return Nothing

            End If

        End Get

    End Property

 

    'Partial methods that validates data

    Private Sub OnShipCountryChanged()

        If String.IsNullOrEmpty(Me.ShipCountry) Then

            Me.AddError("ShipCountry", "You need to specify the ship country")

        Else

            Me.RemoveError("ShipCountry")

        End If

 

    End Sub

 

    Public Sub New()

        AddHandler Me.CustomerReference.AssociationChanged, AddressOf Customer_AssociationChanged

 

        'Sets a default value

        Me.ShipCountry = String.Empty

 

        'Adds an error when the new order is created so that the user is obliged to fix it

        Me.AddError("ShipCountry", "You need to specify the ship country")

 

    End Sub

 

    'This event is raised when the user changes the association between customer and order

    Private Sub Customer_AssociationChanged(ByVal sender As Object, _

                                        ByVal e As CollectionChangeEventArgs)

        If e.Action = CollectionChangeAction.Remove Then

            OnPropertyChanging("Customer")

        Else

            If e.Action = CollectionChangeAction.Add Then

                Me.RemoveError("Customer")

            End If

            OnPropertyChanged("Customer")

        End If

    End Sub

 

End Class

It's worth mentioning the Customer.AssociationChanged event handler which allows checking if the association between customer and order has changed.

End of the first part

Part 5 of the series and the first part on Entity Framework stops here. Today we prepared our Model. In next post I will discuss implementing a service layer that allows ViewModels to work against data, even if the ViewModel does not know it is working against the Entity Framework. As usual, I would be happy if you leave your comments/feedbacks/suggestions. The full source code for the application will be available at the end of the series.

Alessandro

Print | posted on domenica 25 luglio 2010 20:42 | Filed Under [ Visual Studio 2010 Visual Basic Windows Presentation Foundation ]

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 8 and 1 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET