Self-Tracking Entities in Silverlight

In Visual Studio 2010 and .NET 4.0, the Entity Framework team added a T4 template for generating self-tracking entities to provide an easier experience building N-tier applications that need to send entities with changes between tiers. Since we first introduced self-tracking entities as a CTP, one common question is whether these entities are compatible with other platforms such as Silverlight, and if so what are the best practices for structuring your solution. In this blog post, I will build a Silverlight product catalog application using a WCF service and self-tracking entities as the payload between Silverlight and the service.

The Visual Studio Solution

The challenge with using the self-tracking entities T4 template with Silverlight is that not all Silverlight and .NET assemblies can be shared between the two platforms. In Silverlight 4.0, a new feature called assembly portability was introduced that allows some .NET assemblies to be used with Silverlight. Some of the pieces of self-tracking entities such as the DataContract attributes and the ObservableCollection<T> classes are not in portable assemblies so that strategy will not work here.

However, because both Silverlight and .NET support WCF data contract serialization, the data contact of your communication payload can be shared when sending messages between Silverlight and .NET. In practice you can accomplish this if you define a class in .NET that has the same [DataContract] and [DataMember] attributes as a similar class that you define in Silverlight; the WCF serializers and deserializers take care of the rest. In the case of self-tracking entities, we will need to have two references to the self-tracking entities code in our solution: one reference from a .NET project and one reference from a Silverlight project.

To get started building the product catalog application, I opened Visual Studio 2010 and created a new Silverlight Application project called “ProductCatalog”.

clip_image002

If you don’t have the latest Silverlight developer tools installed, you will be prompted by Visual Studio to install these, or you can go here to download them.

Visual Studio next brings up a dialog asking how you want to host your Silverlight application. I chose to go with the defaults and host the Silverlight application in a new ASP.NET Web Application Project called ProductCatalog.Web.

clip_image003

After pressing ok, this gave me a Silverlight Application project, and a .NET ASP.NET Web Application project in the solution explorer:

clip_image004

Next it’s decision time as there are a few options for how to expose the service and the entities you pass between the service and the Silverlight application. First let’s consider where to put the service. The options you have are to either add a service control to the ProductCatalog.Web project, or to add a new WCF project to your solution to keep your services. The decision comes down to how you plan to host your web site and how you are already managing other services in your application or business. To keep things simple, we’ll just add the WCF service to the ProductCatalog.Web project.

To do this, right click on the ProductCatalog.Web project and choose Add | New Item… In the Add New Item dialog box, click on the Silverlight category and then choose to add a Silverlight-enabled WCF Service. I’ve chosen to name mine CatalogService.svc.

clip_image006

The final decision to make for setting up your Visual Studio solution is where to keep the Silverlight version of your entities. Again there are two options: build them into the existing Silverlight application, or create a new Silverlight class library that you can reference in your Silverlight application. If this is the only Silverlight application that will use your entities, it is fine to just build them into your existing Silverlight application project. If you plan to share the entities with other Silverlight applications or with other Silverlight class libraries, you should put them in their own class library, which is what I’ll do for our product catalog application.

To add a Silverlight class library, right click on the Visual Studio solution and choose Add | New Project … In the Add New Project dialog select the Silverlight category and choose the Silverlight Class Library project  type.  I named mine SilverlightEntities.

clip_image008

When you click OK, you are prompted to choose the version of Silverlight you are targeting and in our case this is Silverlight 4.

clip_image009

You can remove the Class1.cs file that is added to this project by default.

The final step is to have the Product Catalog Silverlight application reference the SilverlightEntities class library. To do this right click on the References folder in the ProductCatalog project and choose Add Reference… From the Add Reference dialog, click on the Projects tab and select the SilverlightEntities project. This will set up the reference so you can use classes from the SilverlightEntities project within the ProjectCatalog Silverlight application.

Once you’ve completed these steps, your solution should look like this below, with a ProductCatalog project, a SilverlightEntities project, and a ProductCatalog.Web project with a CatalogService.svc file added.

. clip_image010

Adding a Model

The database the Product Catalog application will use is the Northwind sample for SQL Server. The Entity Framework is part of .NET and so the model we build will be part of the .NET ProductCatalog.Web ASP.NET project.  To add the model to this project, right click on ProductCatalog.Web and choose Add | New Item… From the Add New Item dialog, select the Data category and choose the ADO.NET Entity Data Model item type. Call the file Model.edmx.

clip_image012

In the Entity Data Model Wizard, choose to generate a model from the database and choose the Products and Categories tables to include in the model. This adds a Model.edmx file to your project and opens the entity designer.

Next it’s time to start working with self-tracking entities so that we can write the service, and then build the Silverlight UI for our application. Right click on the entity designer surface and select Add Code Generation Item . In the Add New Item dialog, select the Code category and then pick the ADO.NET Self-Tracking Entity Generator as the item. I’ve named mine Model.tt.

clip_image014

When you click Add, you may get a warning about running T4 templates, and you can click Ok to close the warning dialog. Now there is a model in your solution, and the C# code that is generated for these entities is self-tracking entity code. We are all set to write a service that sends self-tracking entities to the Silverlight application and receives entities with changes that we want to save.

Building the Service

The service for the Product Catalog will be fairly simple with only two methods: a method for retrieving the product catalog and a method for saving changes to the product catalog. Both methods will work by sending around an ObservableCollection<Category> where each Category has a collection of its associated Product entities.

Open the CatalogService code file which is underneath the CatalogService.svc file. The code for the two service methods on CatalogService can be found below (you will need to add a “using System.Collections.ObjectModel;” to the top of the file for it to compile). There are a few interesting points about this code. In the GetCatalog method, all that is needed to retrieve the catalog is to query for all the Category entities and include each one’s Products entities. The UpdateCatalog takes an ObservableCollection<Category>and for each Category instance, calls the ApplyChanges method to extract the changes made to the self-tracking entity and put them in the NorthwindEntities ObjectContext. ApplyChanges operates on all related entities, so even if a Category entity is unchanged, ApplyChanges will also look at all of the Category’s Products and apply any changes it finds to the ObjectContext.  Once all changes have been applied, the changes are saved to the database.

[ServiceContract(Namespace = "")]

[AspNetCompatibilityRequirements(RequirementsMode =

                                 AspNetCompatibilityRequirementsMode.Allowed)]

public class CatalogService

{

    [OperationContract]

    public ObservableCollection<Category> GetCatalog()

    {

        using (var ctx = new NorthwindEntities())

        {

            return new ObservableCollection<Category>(

                                 ctx.Categories.Include("Products"));

        }

    }

 

    [OperationContract]

    public ObservableCollection<Category> UpdateCatalog(

                                   ObservableCollection<Category> categories)

    {

        using (var ctx = new NorthwindEntities())

        {

            foreach (var c in categories)

            {

                ctx.Categories.ApplyChanges(c);

            }

            ctx.SaveChanges();

        }

        return GetCatalog();

    }

}

It is worth pointing out that the UpdateCatalog returns a new product catalog via the GetCatalog method. With self-tracking entities you have to make a decision on whether to continue using the same entities in the client tier, or replace them with refreshed versions on update calls. There are advantages and disadvantages to both and I plan to write a follow-up “best practices, tips and tricks” blog series on self-tracking entities that will address this in more detail (for example, which option to choose if you have store generated keys).

One downside to sending large sets of entities between a client and the service is that this can lead to a large WCF message size. By default, WCF services do not have a large enough message size set for receiving sets of self-tracking entities so you will need to change the service’s web.config file to have a larger message size. In the fragment below, add maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" to the empty httpTransport node that the service generated:

      <customBinding>

       <binding name="ProductCatalog.Web.CatalogService.customBinding0">

          <binaryMessageEncoding />

          <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"/>

        </binding>

      </customBinding>

 

Your service is now set up to send and receive sets of self-tracking entities.

Building the SilverlightEntities Class Library

The first step in building the Product Catalog Silverlight application is to include all of the self-tracking entity code that was generated as part of your model into a Silverlight assembly. By default, the self-tracking entity template generates C# or VB code that can build in a Silverlight project. In our case, we’ll reference the code generated for the entities from the SilverlightEntities Silverlight class library. Visual Studio provides a way to add a link from a project to source files, and we’ll take advantage of this to include the self-tracking entity code files into the SilverlightEntities project. To do this, right click on the SilverlightEntities project and select Add | Existing Item … From the Add Existing Item dialog, navigate to the ProductCatalog.Web project and select the Model.tt file (this is the template that generates all of the self-tracking entities). Next, instead of just clicking “Add”, click on the down arrow on the Add button and select Add As Link.

clip_image015

What this does is to give control of running the Model.tt T4 template to both the ProductCatalog.Web and the SilverlightEntities project. This is a nice convenience because if you modify the model (for example, by adding an additional entity or property), both the .NET and the Silverlight versions of your entities will stay in sync. However, the generated code will use the default namespace of the project that triggered code generation so if the two projects have different default namespaces, you can get yourself into trouble. To avoid this, right click on the SilverlightEntities project and choose properties. In the project properties dialog, make the default namespace for the SilverlightEntities project “ProductCatalog.Web”.

Alternatives are to copy all the code files to your SilverlightEntities folder, but keep in mind that if you do that, then the SilverlightEntities and ProductCatalog.Web versions will not be kept in sync automatically.

Before you can build your solution or project, you’ll need to add a reference to System.Runtime.Serialization to the SilverlightEntities project so that project knows about DataContractAttribute and DataMemberAttribute for WCF serialization.

The Product Catalog Silverlight Application

The final step is to hook the Silverlight application to the service we created and then build a UI that retrieves and displays the product catalog, allows updates, and then uses the service to push changes back to the database.

To hook the ProductCatalog Silverlight application to the CatalogService, right click on the ProductCatalog project and select Add Service Reference. Click on the Discover button and choose the CatalogService.svc service. You can give the service reference a namespace; for example I named mine “ServiceReference”.

clip_image016

You can then build the UI for your Silverlight application, such as the simple one I’ve built below with a ComboBox drop down for the categories, and an editable GridView for the products. Self-tracking entities support many of the common data binding mechanisms such as INotifyPropertyChanged and ObservableCollection<T>, so it’s pretty easy to bind the entities directly to the UI controls.

clip_image017

When the page initializes, I connect to the service and retrieve the product catalog using the GetCatalog method on the service:

public MainPage()

{

    InitializeComponent();

 

    CatalogServiceClient service = new CatalogServiceClient();

    service.GetCatalogCompleted += (object s, GetCatalogCompletedEventArgs args) =>

                                   { SetCatalog(args.Result); };

    service.GetCatalogAsync();

}

 

The SetCatalog method is a helper method I wrote in the MainPage class to bind the categories and products entities to the controls. This method stores the new collection of Category entities into a class field on the MainPage class called _catalog.

private void SetCatalog(ObservableCollection<Category> newCatalog)

{

    int selectedIndex = CategoriesComboBox.SelectedIndex;

    _catalog = newCatalog;

    MainGrid.DataContext = _catalog;

    CategoriesComboBox.SelectedIndex = selectedIndex;

}

 

Finally, the Save button’s Click handler simply passes the updated set of category and product entities to a call to the UpdateCatalog method on the service:

private void SaveButton_Click(object sender, RoutedEventArgs e)

{

    try

    {

        CatalogServiceClient service = new CatalogServiceClient();

        service.UpdateCatalogCompleted += (object s,

                                           UpdateCatalogCompletedEventArgs args) =>

                                 {

                                     SetCatalog(args.Result);

                                     MessageBox.Show("The catalog saved successfully.");

                                 };

        service.UpdateCatalogAsync(_catalog);

    }

    catch (Exception ex)

    {

        MessageBox.Show(string.Format("An error occured while saving the

                        catalog:{0}{1}", Environment.NewLine, ex.Message));

    }

}

 

In Summary

Self-tracking entities provide an easy way to move sets of entities with changes between Silverlight applications and .NET services. Once you have your Visual Studio set up, you can share the code generated by the self-tracking entities T4 template between .NET and Silverlight projects while still retaining the correct WCF data contract. Self-tracking entities have some nice built in capabilities for Silverlight such as the ability to do two-way data binding with UI controls. While we work to make sharing code between Silverlight and .NET easier, we’d appreciate any feedback you have about self-tracking entities in Visual Studio 2010 or capabilities you’d like to see in a future release.

Jeff Derstadt

Entity Framework Team