• Beth Massi - Sharing the goodness

    Working with a Local Database File in Visual Studio

    • 7 Comments

    In this post and video I’ve shown folks how to work with a local database file (.mdf, .mdb, .sdf) that resides directly inside your project in order to see updates made while debugging your application. If it looks like everything saves fine while debugging, but you can’t see the updates if you look directly in the database, then there’s a simple fix. Today the VS Data Team wrote up a great blog post on this as well so check it out!

    In general I prefer to develop against attached SQL Server databases because it’s easier to debug. But if you’re working with SQL Compact or Access databases you’ll need to be aware of how the database file is copied out with your application EXE and what file you are really working with.

    Enjoy!

  • Beth Massi - Sharing the goodness

    Visual Basic Power Packs also included with Visual Studio 2010

    • 4 Comments

    If you’re like me and used to having line & shape controls, PrintForm and a handy DataRepeater as part of your toolbox then you’ll be excited to know that these controls are also going to be installed with Visual Studio 2010.

    On Friday, Yunfeng Dong mentioned on the VS Data team blog that the VB Power Packs 3.0 release was installed by default with Visual Studio 2008 SP1. In the comments of that post someone asks about VS2010 availability and it’s good to know that these controls will also be available in that release.

    However you don’t need Visual Studio 2008 SP1 or Visual Studio 2010 to use these controls. If you have Visual Studio 2005 you can download the web release here. (Note that if you are using Express versions of Visual Studio, the controls will not automatically show up in your toolbox. Take a look at the readme on how you can add them.)

    Enjoy!

  • Beth Massi - Sharing the goodness

    WPF or Silverlight? Billy Hollis Shares His Thoughts on Channel 9

    • 3 Comments

    One of my favorite Visual Basic MVP’s, Billy Hollis, was caught at TechEd by the Patterns&Practices folks for a quick chat. In this short 2-minute interview Billy shares his opinions on WPF and Silverlight. Check it out:

    Billy Hollis on WPF and Silverlight

    By the way, anyone notice Channel 9 turned blue today in honor of Windows 7…

    Enjoy!

  • Beth Massi - Sharing the goodness

    Filtering Child DataTables Using TableAdapters

    • 3 Comments

    Since my last post on filtering child collections using Entity Framework, I have had more than a few emails about how to load filtered child DataTables that are part of a master-detail relationship and then get them to save properly through the TableAdapters. Many people are running into the problem where the designer won’t automatically generate the update/insert/delete commands for you when you are writing a select statement that uses multiple tables. Of course you could specify your own statements manually (and often you need to because your Select statement is too complex) but in this case there’s a much better way to specify this filter and still get the benefit of the designer creating your commands.

    For instance, let’s take the same database tables Customer and Orders that we’ve been working with:

    NOTE: If you are unfamiliar with how to set up and work with master-detail (parent-child) tables check out this "How Do I" video first.

    So say we want to only pull up the Orders on the Customer who’s LastName is “Massi” -- meaning we want to filter both the parent (Customer) and child (Orders) on Customer.LastName. There’s a couple ways to do this.

    Specifying a New Query

    TableAdapters are great at managing your queries. By default when you use the designer to select related tables it will automatically create default Fill and GetData select methods for you as well as generate the update//insert and delete statements for you. If you look in the properties for the TableAdapter you will see a SelectCommand generated for you that selects ALL the fields and ALL the rows of the table in the database. In 99.999% of the cases you want to filter these rows for scalability reasons. (And I urge you to do so).

    image

    Also notice that Visual Studio has created Insert/Update/Delete commands as well for us. We can easily specify a new query that filters our rows and still take advantage of these update/insert/delete commands. We want to do this for both our tables.

    First right-click on the child TableAdapter (in this case OrdersTableAdapter) and select Add –> Query. Then choose your command type, here I’ll leave the default “Use SQL Statements”. Click Next. Then choose your query type, in this case choose the default “SELECT which returns rows”. Click Next. Now you can write your select statement.

    It is VERY IMPORTANT that you only select the fields from the child (Orders) table and not include fields from other tables in the result set. Otherwise the generated commands will not work. You can filter the rows any way you like with a WHERE clause (and you definitely should) but keep in mind that if you select fields from other tables to be included in the result set, then you will need to specify your own Insert\Update\Delete commands above.

    To write our filter on the child Orders table I’ll use the command builder to join the Customer table and then use the LastName in the WHERE clause.

    image

    Here’ I’m creating a parameterized query so that I can specify the LastName on the Form and not hard-code it here. Notice that the structure of the result set (the fields in the SELECT clause above) exactly match the fields in the OrdersDataTable.

    SELECT  Orders.OrderID, 
            Orders.CustomerID,
            Orders.OrderDate,
            Orders.ShipDate,
            Orders.Modified
    FROM    Orders INNER JOIN
            Customer ON Orders.CustomerID = Customer.CustomerID
    WHERE  (Customer.LastName = @LastName)

    Click OK to get out of the Query Builder (if you were in there) and then Click Next. Now you get to choose which methods to generate and what they should be named. For this example I named them FillByLastName and GetDataByLastName. This creates methods on the TableAdapter that you can now use to query the child rows.

    Now repeat this process for the parent table (Customer). Select the CustomerTableAdapter in the dataset designer, right-click and select Add –> Query. Same steps as before except the SELECT statement is much easier, no JOIN necessary, just a filtered WHERE clause.

    SELECT CustomerID, LastName, FirstName, Address, City, State, ZIP, Modified
    FROM   Customer
    WHERE  LastName = @LastName

    I named the methods the same as I did for the child, FillByLastName and GetDataByLastName.

    Now you can design your master-detail form. From the Data Sources Window expand and then drag the parent (Customer) and then drag the related Orders (the one under Customer) onto your Windows form. Basically design your winform like normal and let Visual Studio generate the code to load and save for you.

    The only thing you have to do now is change the method names on the TableAdapters (and make sure they load in parent then child order) in the form Load handler:

    Private Sub Form1_Load() Handles MyBase.Load
        Me.CustomerTableAdapter.FillByLastName(Me.OMSDataSet.Customer, "Massi")
        Me.OrdersTableAdapter.FillByLastName(Me.OMSDataSet.Orders, "Massi")
    End Sub

    Everything else will work like before but now we are filtering both the tables. You can add as many new queries to the TableAdapters as you need. As long as the fields in your query (SELECT clause) match the structure of your generated DataTable then you can use the generated update/insert/delete commands.

    But what if you already have hundreds of forms that use the default Fill or GetData methods and you really need to set up a filter on these SelectCommands because you don’t want to have to change all your forms? That’s pretty easy too.

    Filtering the Default GetData and Fill Methods

    If we want to modify the default Fill and GetData methods to support this filter then there’s a little trick we can do. Once the DataSet is set up with your “unfiltered” parent-child tables, open up the properties window and expand the SelectCommand (like pictured above) and edit the CommandText property for both of the TableAdapters. Specify the same select statements as above. Once you modify the property Visual Studio will ask you “Do you want to regenerate updating commands based on the new command text? (Current updating commands will be overwritten)”. Just say NO.

    Now the calls to GetData and Fill will have a filter set but the generated commands will not be overwritten!

    I hope this clears things up for folks. Remember that it’s best practice to put filters on your queries to narrow your result sets – this puts a lot less strain on your databases and networks. There’s a couple tricks you need up your sleeve to do this in Visual Studio, but it is the way it is so that the dataset designer can handle complex and completely custom scenarios too, like multi-table views and stored procedures. Hmmm… maybe it’s time for a How Do I video on that…. ;-)

    Enjoy!

  • Beth Massi - Sharing the goodness

    Channel 9 Interview: LINQ Language Deep Dive with Visual Studio 2008

    • 11 Comments

    Ever wonder what really happens when you write a simple LINQ query?

    Dim query = From row In db.Customers _
                Where row.Country = "Canada" _
                Select row

    A lot of new language features went into the compilers in Visual Studio 2008 to make even this simple LINQ query work.

    In this interview I sit down with Jonathan Aneja, a Program Manager on the Visual Basic Compiler team, who dives deep into these features like Type Inference, Anonymous Types, Lambda Expressions, Expressions Trees, and more. He explains what's actually happening behind the scenes and all the work the compiler is doing for you when you write a LINQ query.

    My head almost explodes at the end but I learned a lot of important concepts so check it out:

    LINQ Language Deep Dive with Visual Studio 2008

    image

    Enjoy!

  • Beth Massi - Sharing the goodness

    Filtering Entity Framework Collections in Master-Detail Forms

    • 3 Comments

    Last post I talked about how to get WPF data binding to work with master-detail entity collections. I had a couple readers ask me how they could filter the child collection instead of bringing them all down so in this post I’d like to show how you could do that. It’s actually pretty easy.

    Customer (Master) –< Orders (Detail)

    I created a very simple Entity Data Model (EDM) that demonstrates a Master-Detail relationship. The Orders table is defined in the database with a non-nullable foreign key CustomerID, meaning that no Order can exist without a Customer. This relationship is inferred by Entity Framework (EF) to set up the navigation properties giving us an Orders EntityCollection on Customer.

    To recap, I’ve created my own ObservableCollection(of Customer) called CustomerCollection so that when the UI modifies the collection through AddItem/RemoveItem, we can instruct the ObjectContext to track these changes so that it will insert and delete entities to the database when we call SaveChanges.

    The constructor takes an IEnumerable(Of Customer) which allows us to pass a LINQ to Entities query into our CustomerCollection. This executes the query against the database when we call the base class’s constructor which iterates over the query results and adds the Customer entities to our collection.

    Take a look at the first chunk of code in the last post for the full code listing for the CustomerCollection class. I don’t have to make any changes to it to support filtering the children.

    Loading All Detail Entities

    If you want to bring down all the Orders for the selected Customer you can use the .Include extension like I’ve shown before using explicit loading.

    'EF ObjectContext connects to database and tracks changes
    Dim db As New OMSEntities
    'inherits from ObservableCollection(Of Customer)
    Dim CustomerData As CustomerCollection
    
    'Include all Orders for Customer #1
    Dim customers = From c In db.Customers.Include("Orders") _
                    Where c.CustomerID = 1
    
    CustomerData = New CustomerCollection(customers, db)

    This ends up shooting one query to SQL Server to return all the Orders for the selected Customer when we pass the query into our collection. But what if we also wanted to filter the Orders? There’s a couple ways we can do this because of a cool feature of the Entity Framework.

    Filtering Detail Entities with a Sub-Query

    When the entities are being created in memory on the client from the query (materialized), the object state manager will attempt to hook up the entity references and collections automatically. This means that all we have to do is specify a filtered query and make sure we execute it by enumerating over the results (or calling ToList).

    So say we wanted to only grab the most recent Orders where the order was placed on or after January 1st, 2009:

    Dim query = From c In db.Customers _
                Where c.CustomerID = 1 _
                Select Customer = c, _
                       Orders = From o In c.Orders _
                                Where o.OrderDate >= #1/1/2009#
    
    Dim customers = From item In query.ToList Select item.Customer
    
    CustomerData = New CustomerCollection(customers, db)

    Here I’m specifying a sub-query to only pull the Orders I want on the selected Customer. The first query creates a collection of anonymous types that have a Customer object and a filtered Orders collection. In the following query, I need to call query.ToList in order to execute the query against the database and materialize the objects. This sends one statement to SQL Server like before, but this time it’s filtered on Orders as well.

    At this point the Customers and Orders are in memory and the object state manager has hooked them up for us. Meaning that the Customer now has an Orders collection like we want. As long as we Select the entire Customer and Order types in our queries this will work (as opposed to just selecting specific fields – the entity types have to match up for it to work). The second query is just a LINQ to Objects query (in memory) that we’re using so that we can pass an IEnumerable(Of Customer) to our ObservableCollection.

    Filtering Detail Entities with Separate Queries

    There are other ways we could write this. We could write the queries separately:

    Dim customers = (From c In db.Customers _
                     Where c.CustomerID = 1).ToList()
    
    Dim orders = (From o In db.Orders _
                  Where o.Customer.CustomerID = 1 AndAlso _
                        o.OrderDate > #1/1/2009#).ToList()
    
    CustomerData = New CustomerCollection(customers, db)

    Notice that I’m calling ToList to force the queries to execute so that the objects will materialize (come into memory), then I’m just passing the Customers to the constructor because at this point the object state manager has hooked up our entities (Customer has an EntityCollection of Orders based on the filtered query). The results on the client are the same but the major difference here is that two statements are sent to SQL Server, so keep that in mind. I like the first approach better with a sub-query because of the single SQL statement that’s generated as well as the clearer looking LINQ query.

    I’ve updated this sample application that also shows a variety of other data binding techniques with EF and WPF so have a look. The filter example above is in the SimpleMasterDetailBinding form.

    Enjoy!

  • Beth Massi - Sharing the goodness

    Master-Detail Data Binding in WPF with Entity Framework

    • 27 Comments

    Today I thought I would talk about a really common scenario in data applications, creating a master-details (one-to-many) data entry form. I’ve written about WPF data binding and Entity Framework a lot in the past:

    Posts:

    Videos:

    Today I want to pull these concepts together and walk through one way to create a master-detail form in WPF using entities from the Entity Framework. Specifically, we’ll declare CollectionViewSources in our XAML like I showed here, to bind to an ObservableCollection of entities like I showed here, where the children are explicitly loaded like I showed here. Everybody got that? ;-)

    Creating the Entity Data Model

    First let’s create a simple Entity Data Model (EDM) that demonstrates a Master-Detail relationship. I’ll use a simple database called OMS that has Customer and Orders tables with a non-nullable foreign key set up between them on CustomerID, meaning that no Order can exist without a Customer. This relationship is inferred by Entity Framework (EF) to set up the navigation properties. Notice that there is an Orders EntityCollection on Customer.

    What we want to do is build a simple form that will let us Edit, Add, and Delete Customers and their Orders. First let’s set up the WPF Data Binding in XAML.

    Defining the CollectionViewSource and Data Bindings

    To recap, a CollectionViewSource is a proxy for the CollectionView which manages the currency (the position) in the list of entities. It has a property called Source which can be set in our code behind. This way, we can set up CollectionVieSources in XAML for all our data lists and bind them to the corresponding controls all in XAML. Then at runtime in our code we set the Source properties and only at that time does the data pull from the database.

    To define a Master-Detail relationship we define two CollectionViewSources one for the master and one for the detail collections. Then on the detail we set the Source property to the master CollectionViewSource and then specify the Path property as the name of the child collection. In our case the name of the collection on Customer is “Orders”. So we can specify the XAML like so:

    <Window.Resources>
        <CollectionViewSource x:Key="MasterViewSource" />
        <CollectionViewSource x:Key="DetailsViewSource" 
                        Source="{Binding Source={StaticResource MasterViewSource}, Path='Orders'}" />
    </Window.Resources>

    Now as the position changes in the MasterViewSource to point to a new Customer, the DetailsViewSource will filter automatically to only those related Orders for that Customer. We can now set the rest of the data bindings on the controls on the form by setting the BindingContext of the container controls to the CollectionViewSource we want to display. For example, we can set up a StackPanel to contain the Customer fields and set the StackPanel.DataContext to the MasterViewSource. Under that we can set up a ListView to display the Orders by setting the ListView.ItemsSource to the DetailsViewSource.

    <Grid>
    ...
    <StackPanel Name="StackPanel2"
    Grid.Column="1"
    DataContext="{Binding Source={StaticResource MasterViewSource}}"> <TextBox Name="TextBox1" IsReadOnly="True" Text="{Binding Path=CustomerID, Mode=OneWay}"/> <TextBox Name="TextBox5"
    Text="{Binding Path=LastName}"/>
    ...
    </StackPanel> ...
    <ListView Grid.Row="3" Name="ListView1" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Source={StaticResource DetailsViewSource}}"> <ListView.View> <GridView> <GridViewColumn Header="ID" Width="75"> <GridViewColumn.CellTemplate> <DataTemplate> <Label Content="{Binding Path=OrderID}" Margin="-6,0,-6,0"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> <GridViewColumn Header="Order Date" Width="100"> <GridViewColumn.CellTemplate> <DataTemplate> <TextBox Text="{Binding Path=OrderDate}" Margin="-6,0,-6,0"/> </DataTemplate> </GridViewColumn.CellTemplate> </GridViewColumn> ...

    The only thing we need to do now is set the Source property of the MasterViewSource in code to the collection of our Customer entities.

    Defining the Master-Detail Entities in an ObservableCollection

    I showed before how we can create a collection of entities that inherits from ObservableCollection in this post to make it easier to work with WPF data binding. But in that example we were only working with a simple collection of Customers and not their Orders. If you recall, the ObjectContext is what tracks changes on entities so in order for the ObjectContext to be notified that adds and deletes to the ObservableCollection need to be tracked you need to override the InsertItem and RemoveItem methods so that you can tell the ObjectContext to either add or delete the entity which will ultimately execute against the database. In the constructor I pass a reference to the ObjectContext. You can also pass in any collection of entities, say from a LINQ query, and then add them to the ObservableCollection. However, we need to make a couple modifications to our collection so that we can also track the child order entities correctly.

    Adds to the Customer.Orders EntityCollection will will cause the addition of a new Order to the collection as well as the association to Customer automatically. However removing the Order from the Customer.Orders EntityCollection will only remove the association and will not attempt to actually delete the Order from the database. Instead it attempts to set the CustomerID to NULL (to remove the association from the Customer) but since we have referential integrity set up to disallow this we will get an error if we attempt to SaveChanges.

    In a lot of scenarios it makes sense to just remove the association and set the foreign key to NULL in the database. But in this example we really mean to delete the Order record completely when the Order is removed from the collection. So the key is adding an event handler to the AssociationChanged event on the Orders EntityCollection that’s hanging off our Customer entity and telling the ObjectContext to explicitly delete the Order.

    Public Class CustomerCollection
        Inherits ObservableCollection(Of Customer)
    
        Private _context As OMSEntities
        Public ReadOnly Property Context() As OMSEntities
            Get
                Return _context
            End Get
        End Property
    
    
        Sub New(ByVal customers As IEnumerable(Of Customer), ByVal context As OMSEntities)
            MyBase.New(customers)
            _context = context
    
            For Each c In customers
                AddHandler c.Orders.AssociationChanged, AddressOf Orders_CollectionChanged
            Next
        End Sub
    
        Protected Overrides Sub InsertItem(ByVal index As Integer, ByVal item As Customer)
            AddHandler item.Orders.AssociationChanged, AddressOf Orders_CollectionChanged
    
            'Tell the ObjectContext to start tracking this customer entity
            Me.Context.AddToCustomers(item)
            MyBase.InsertItem(index, item)
        End Sub
    
        Protected Overrides Sub RemoveItem(ByVal index As Integer)
            Dim customer = Me(index)
            RemoveHandler customer.Orders.AssociationChanged, AddressOf Orders_CollectionChanged
    
            For i = customer.Orders.Count - 1 To 0 Step -1
                'When deleting a customer, delete any orders if any exist
                Me.Context.DeleteObject(customer.Orders(i))
            Next
    
            'Tell the ObjectContext to delete this customer entity
            Me.Context.DeleteObject(customer)
            MyBase.RemoveItem(index)
        End Sub
    
        Private Sub Orders_CollectionChanged(ByVal sender As Object, _
                                             ByVal e As CollectionChangeEventArgs)
            If e.Action = CollectionChangeAction.Remove Then
                'Adding an order to a customer is handled automatically 
                ' for us but we need to tell the ObjectContext to delete the order
                ' if an order is removed from the Orders EntityCollection 
                Me.Context.DeleteObject(CType(e.Element, Order))
            End If
        End Sub
    End Class
    

    Loading the Master-Detail Entities

    Finally we’re ready to write a LINQ query to load the entities into our CustomerCollection and then set that as the Source property of the MasterViewSource. In this example I’m loading the Orders explicitly by calling .Include(“Orders”) on the LINQ query which constructs a single statement to retrieve the Customer and all their Orders from the database. I discuss explicit load in this post.

    We can then grab a reference to the MasterViewSource & DetailViewSource’s View property in order to add/remove items in the collections. When we’re done, we can call SaveChanges on the ObjectContext and the database will be updated.

    Private db As New OMSEntities 'EF ObjectContext connects to database and tracks changes
    Private CustomerData As CustomerCollection 'inherits from ObservableCollection
    
    Private MasterViewSource As CollectionViewSource
    Private DetailViewSource As CollectionViewSource
    
    'provides currency to controls (position & movement in the collections)
    Private WithEvents MasterView As ListCollectionView
    Private DetailsView As BindingListCollectionView
    
    Private Sub Window_Loaded() Handles MyBase.Loaded
    
        Dim query = From c In db.Customers.Include("Orders") _
                    Where c.CustomerID = 1 _
                    Select c
    
        Me.CustomerData = New CustomerCollection(query, db)
    
        Me.MasterViewSource = CType(Me.FindResource("MasterViewSource"), CollectionViewSource)
        Me.DetailViewSource = CType(Me.FindResource("DetailsViewSource"), CollectionViewSource)
        Me.MasterViewSource.Source = Me.CustomerData
    
        Me.MasterView = CType(Me.MasterViewSource.View, ListCollectionView)
        Me.DetailsView = CType(Me.DetailViewSource.View, BindingListCollectionView)
    End Sub
    Private Sub MasterView_CurrentChanged() Handles MasterView.CurrentChanged
        'We need to grab the new child view when the master's position changes
        Me.DetailsView = CType(Me.DetailViewSource.View, BindingListCollectionView)
    End Sub
    
    Private Sub btnSave_Click() Handles btnSave.Click
        Try
            db.SaveChanges()
            MessageBox.Show("Customer data saved.", Me.Title, MessageBoxButton.OK, MessageBoxImage.Information)
        Catch ex As Exception
            MsgBox(ex.ToString())
        End Try
    End Sub
    
    Private Sub btnDelete_Click() Handles btnDelete.Click
        If Me.MasterView.CurrentPosition > -1 Then
            Me.MasterView.RemoveAt(Me.MasterView.CurrentPosition)
        End If
    End Sub
    
    Private Sub btnAdd_Click() Handles btnAdd.Click
        Dim customer = CType(Me.MasterView.AddNew, Customer)
        Me.MasterView.CommitNew()
    End Sub
    
    Private Sub btnPrevious_Click() Handles btnPrevious.Click
        If Me.MasterView.CurrentPosition > 0 Then
            Me.MasterView.MoveCurrentToPrevious()
        End If
    End Sub
    
    Private Sub btnNext_Click() Handles btnNext.Click
        If Me.MasterView.CurrentPosition < Me.MasterView.Count - 1 Then
            Me.MasterView.MoveCurrentToNext()
        End If
    End Sub
    
    Private Sub btnAddDetail_Click() Handles btnAddDetail.Click
        Dim order = CType(Me.DetailsView.AddNew, Order)
        Me.DetailsView.CommitNew()
    End Sub
    
    Private Sub btnDeleteDetail_Click() Handles btnDeleteDetail.Click
        If Me.DetailsView.CurrentPosition > -1 Then
            Me.DetailsView.RemoveAt(Me.DetailsView.CurrentPosition)
        End If
    End Sub

    Now we can Add, Edit, and Delete Customer and their Orders at the same time and changes will be propagated properly to the database through Entity Framework in one call to SaveChanges. I’ve updated this complete sample application that demonstrates this as well as other aspects of WPF Data Binding with Entity Framework so have a look. 

    UPDATE: Milind talks about some of the tooling improvements in Visual Studio 2010 on the VSData blog regarding building WPF forms against Entity Data Models so check it out --> WPF Data Binding: Creating a Master-Details form in Visual Studio 2010

    Enjoy!

  • Beth Massi - Sharing the goodness

    Visual Basic 10, C# 4 & Visual Studio 2010 Talk @ EastBay.NET

    • 2 Comments

    On Wednesday I spoke at EastBay.NET User’s group at our new Livermore location and I have to say it was a TON of fun. I don’t think I’ve had this much fun speaking in a while. Deborah has an awesome recap of the event. We were afraid that we’d have low attendance because of the switch in venue but it was a great turnout!

    I did the same talk that I had done at DevTeach which I wrote about here. I guess the third time’s a charm because I had people laughing and learning at the same time which is what I always strive for in my talks. I had great feedback that I explained Co/ContraVariance really well. I’ll write up a blog post on that later but I basically just took this example from Lucian and explained it in “OOP 101” terms driving it home with a practical example in the end.

    I also showed all of the C# 4 features in one method that demonstrated COM interop with Office lifted from this walkthrough and demonstrated the “no PIAs” feature (which removes the PITA experience of deployment ;-)) by cracking open reflector and showing the type information embedded into my assembly. Take a look at the end of this walkthrough for info on that feature.

    I also showed off some of the cool features of VS2010 like the new look-and-feel, the new editor, and WPF designer and data tooling. I needed 3 hours to cover everything I wanted to show so I ran out of time in the end but that always happens to me :-).

    Check out the EastBay.NET site for our upcoming events. Hope to see you there next month.

    Enjoy!

  • Beth Massi - Sharing the goodness

    Implementing Validation in WPF on Entity Framework Entities

    • 19 Comments

    I’ve blogged before about implementing validation on LINQ to SQL classes as well as how to customize the display of error messages in WPF. In this post I want to show how you can use these same techniques to validate entities coming from the Entity Framework (EF). Like LINQ to SQL classes, Entity Framework entities are implemented as partial classes so that you can extend them with your own code on top of the code that the designers generate for you. You can extend EF entities in a similar way as LINQ to SQL classes. 

    Creating the Partial Class

    Let’s take the example that I started here in this How Do I video on building a simple data entry form to edit customers. You can download the code for that video here. In this sample I have two projects, one for the WPF client (WpfEfDataEntry) and one for the Data Access Layer (WpfEfDAL) that contains a simple Entity Data Model (edmx) of a little database I created that tracks customers and their orders.

    image

    image

    To extend the Customer class that is generated from the EF designer, right-click on the DAL project and select Add –> Class then name it Customer. This places the class in the same Namespace as the entities that are generated by the designer. This is necessary for partial classes to work. (Partial classes are just a way that you can define one class in multiple physical files and Visual Studio will handle compiling them into one class for you.)

    image Here’s a trick in VB. You know you got your partial class in the right namespace when you drop down the Declarations dropdown and you see the list of partial methods and properties that the class defines.

    Also in VB the Partial keyword is only required on one of the class declarations in one of the files. (In C# it’s required on all of them.) The EF designer generates the Customer class with the partial keyword. If you click the “Show all Files” button on the Solution Explorer toolbar and then expand the .edmx you can open the .Designer file and see the entity Partial Class definitions:

    Partial Public Class Customer    

    You can of course be explicit in VB and add the Partial keyword to all your partial class files as well.

    Adding Validation to the Partial Class

    To add validation we can implement the IDataErrorInfo interface in our customer partial class. Using this interface will make validation errors display in Winforms as well as WPF so I tend to prefer this implementation over others like ValidationRules collection in WPF. For this example let’s make sure that the LastName field isn’t empty but we’ll also provide a default value by specifying it in the constructor. This code is the same code we would use if we were working with LINQ to SQL classes.

    Imports System.ComponentModel
    
    Partial Public Class Customer
        Implements IDataErrorInfo
    
    #Region "IDataErrorInfo Members"
        Private m_validationErrors As New Dictionary(Of String, String)
    
        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
    
        Private Sub RemoveError(ByVal columnName As String)
            If m_validationErrors.ContainsKey(columnName) Then
                m_validationErrors.Remove(columnName)
            End If
        End Sub
    
        Public ReadOnly Property HasErrors() As Boolean
            Get
                Return m_validationErrors.Count > 0
            End Get
        End Property
    
        Public ReadOnly Property [Error]() As String _
    Implements
    System.ComponentModel.IDataErrorInfo.Error Get If m_validationErrors.Count > 0 Then Return "Customer 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 #End Region Public Sub New() 'Set defaults Me.LastName = "[new]" End Sub

     

    Now we can write our validation code to check the LastName field. If you look back at the generated Customer class in the .Designer file, notice that there are OnFieldNameChanging and OnFieldNameChanged methods that are also declared as Partial. These are partial methods, a new feature introduced with Visual Studio 2008, that allow you to supply additional code that is called from the generated class. The Changing/Changed methods are called in the property setters. We’ll define the OnLastNameChanged to make sure the user enters a LastName:

        ''' <summary>
        ''' This method is called in the LastName property setter of the customer
        '''  partial class generated by the Entity Data Model designer.
        ''' </summary>
        Private Sub OnLastNameChanged()
            'Perform validation. 
            If _LastName Is Nothing OrElse _LastName.Trim() = "" OrElse _LastName.Trim() = "[new]" Then
                Me.AddError("LastName", "Please enter a last name.")
            Else
                Me.RemoveError("LastName")
            End If
        End Sub
    
    End Class
    

    Now all we need to do is specify on the binding in the XAML of the WPF form to display the validation errors.

    <TextBox Name="txtLastName" Width="Auto" Height="28" Margin="3" 
             Text="{Binding Path=LastName, ValidatesOnDataErrors=True}"/>

    This is exactly the same as we did in this post when working with LINQ to SQL. Read that post to also see how to change the default error template which controls how the errors are displayed. 

    Adding Validation to Entity References

    As you can see validating scalar properties on EF entities works the same as with LINQ to SQL classes. However what if we wanted to make sure that an entity reference was also specified on an EF entity? I’ve posted before about how to get notified when entity references change. But what if we also want to make sure an entity reference is not empty? For instance, in the case of the Order entity above, how would we write a validation to make sure that the Customer entity reference was specified on the Order before we tried to save?

    Looking back up at the Customer (1)—(*) Order in the diagram above, the Order entity has a reference to its Customer parent as specified by the navigation property. In the database there is a foreign key relationship on CustomerID and that is inferred here by EF. This is a difference from LINQ to SQL classes where the classes contain the foreign keys as scalar properties as well. We can’t validate EF entities the same way because there are no scalar properties for the foreign keys. Instead we need to add an event handler to the AssociationChanged event on the entity reference (like I showed before) and then add in our validation. Remember that the AssociationChanged event will fire twice when we are selecting a new reference, once when the old entity reference is removed and then once when the new one is added.

    Imports System.ComponentModel
    
    Public Class Order
        Implements IDataErrorInfo
    
        Public Sub New()
            'Handle this event so that UI can be notified if the customer is changed
            AddHandler Me.CustomerReference.AssociationChanged, AddressOf Customer_AssociationChanged
    
            'Set defaults
            Me.OrderDate = Date.Today()
            'Customer is required 
    Me.AddError("Customer", "Please select a customer.") End Sub 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

    imageWhat I’m doing is putting the Order in an immediate error state so that the user can see that a Customer must be selected on the Order before it is valid. The error will only go away once they select a Customer.

    I’ve created a sample application that you can download from Code Gallery that demonstrates using EF with WPF in a variety of ways including this example so have a look. Also make sure you check out these How Do I videos on EF and WPF.

    Enjoy!

  • Beth Massi - Sharing the goodness

    Look Who’s Blogging!

    • 1 Comments

    I guess Robin and I inspired her on Monday when we had dinner together. Yes folks, Deborah Kurata (VB MVP) is finally spilling her technical heart out on her new blog! Time to update your RSS readers….

    Deborah's Developer MindScape

    Neat-o title too.

    Enjoy!

Page 1 of 2 (11 items) 12