Partager via


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

Comments

  • Anonymous
    May 15, 2010
    Hi, many thanks for this really useful post. It would appear that this achieves much of what WCF  RIA Services set out to achieve, which raises the question on what the pros and cons of using either are?

  • Anonymous
    May 16, 2010
    Jeff, Jason brings up a good point which is something I have wondered about myself ever since I started reading about RIA and self tracking entities.   What are the pro's and con's of both?  And is it possible to use both in the same project?

  • Anonymous
    May 16, 2010
    I had a question. Would this work, if my project is structured where I have NorthWindData which contains my edmx and Objectcontext, a separate class library called NorthWindEntities which contains my Self Tracking Entitie. Those STE are then shared between the web project and silverlight project. This way if i add a new wpf project i can just add a reference to NorthWindEntities. Zeeshan

  • Anonymous
    May 26, 2010
    The comment has been removed

  • Anonymous
    May 26, 2010
    @Zeeshan The class library that contains the self-tracking entities cannot be shared between your .NET web project and your Silverlight project because System.Runtime.Serialization is not a portable assembly. However, you can share your .NET version between the .NET web project and your WPF project. Jeff

  • Anonymous
    May 27, 2010
    Thank you Jeff!

  • Anonymous
    August 16, 2010
    Hi Jeff Very useful post, but could you please give us a link to a functional sample project ? Dan

  • Anonymous
    August 19, 2010
    "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." What does it means? Do we have to always use ObservableCollection<T> in order to STE works ? Can I use List<T> instead ? How if my service just return T (ie. person) NOT ObservableCollection<person>, can STE still works with 2way UI binding ? And yes this is very useful post Jeff, well done !

  • Anonymous
    August 26, 2010
    You lost me...

  • You can not add reference to the Entity namespace in a Silverlight Class Library
  • You can not compile STEs without reference to System.Data.Entity, compilation will fail missing the references. Therefore, this 'solution' is either presented incomplete or simply will not work as described in this article. The so called SilverlightEntities Class Library will not build because it lacks reference System.Data.Entity. What say you?
  • Anonymous
    August 26, 2010
    ...continued information You also can not reference a required namespace System.Data.Objects

  • Anonymous
    September 02, 2010
    Doug, Self-tracking entities consist of two code generation templates; one is for generating the entities themselves, and the other is for generating a class derived from ObjectContext + some extensions that allow changes to be applied to instances of that ObjectContext. Only the code that references ObjectContext needs a reference to System.Data.Entity, and it is not something that you would put in the Silverlight assembly. The entities, however, do not require a reference to System.Data.Entity and thus can be placed inside the Silverlight class library. In the article Jeff just adds the entities to the Silverlight library, not the ObjectContext class. Hope that helps, David

  • Anonymous
    September 03, 2010
    Doug- To add to what David said: we expect you'd move the "entities" class to your silverlight project while keeping your objectcontext class in a service project.  The ObjectContext class will always be EF dependent as it has to provide a bridge from your persistent ignorant entities classes to the entity framework infrastructure.  We expect the ObjectContext class to live in a service that your Silverlight assembly uses. Thanks, Tim

  • Anonymous
    September 03, 2010
    This is good but this is a simpliest scenario. In reality things are different. We have project with huge tables. So we have to cache this entities on client side. And after that THE FUN starts. You will have bunch of issues when you try to work with navigation properties of entities, assigning it to objects that was loaded from another server request (another object graph) - that will cause object graphs mixing and will cause issues when you will try to save changes on server like "Accept changes"-exceptions and "Foreign key"-exceptions. The same situation in scenario of working with clone objects. When you create a Editor window, that edits a clone of object - and you assign to it navigation properties objects from another object graph. To work these issues around you have to invent and implement by yourself some Client Object Context concept. Something that will merge object graphs correctly. More over EF Self-Tracking template doesn't works when you remove some object from navigation collection of parent object with relation 1-Many. For instance you have Person->Contacts. When you remove contact from "Person.Contacts" collection, apply changes, and try to save changes on objectcontext - Contact object doesn't removed from DB, and when you reload Person.Contacts, you will see removed contact in this collection. I see ADO.EF Self Tracking as a perspective platform and full of interesting Ideas. But it is too immature to use it in serious projects.

  • Anonymous
    September 21, 2010
    Thank you for this post. We really need a guidance around when to use RIA Services vs WCF Data Services vs Self-tracking entities ! The main difference for me is that with STE you don't have a client side context. With WCF RIA or Data Services, this client context exists, allowing you to have a UnitOfWork approach. Does somebody already work of Client side context for STE ?

  • Anonymous
    November 02, 2010
    Thanks for the post, I tried to implement it but found that the wcf service proxies in SL are utilised rather than the same types in the SL entities class thats added as a reference which prevents the change tracker from becoming enabled. Basically nothing ever gets updated. Anyone else have that issue?

  • Anonymous
    November 02, 2010
    update fixed my own problem. Just in case anyone runs into the same issue, dont forget to add the namespace declaration to the datacontract attribute...  [DataContract(IsReference = true, Namespace = "your.namespace.here")].

  • Anonymous
    November 16, 2010
    I do not quite understand why do you need the Silverlight class library, since objects you use in SL application come from WCF service reference, not the library

  • Anonymous
    November 23, 2010
    The reason you add the library is so that the code generated by the WCF Service Reference will get the Self-Tracking capabilities.  Without the library reference of the STE, the WCF Service Reference will generate POCO code for the Entities that won't have the self-tracking capabilities because the WCF Service is ignorant of this or something to do with the current Silverlight framework.

  • Anonymous
    December 06, 2010
    dpblogs -- Please help. I followed the example step-by-step. Everything looks OK. It builds. When I run it, it just spins-and-spins at startup with the blue Silverlight circle. Nothing happens after that, it just keeps spinning. I have been trying everything I know; but, that is not much. Sigh. I read all the comments to this blog post so far. I added the namespace as one commenter mentioned. I tried using a static port instead of auto-assign for the IP for the service, because I was doing that with Silverlight 3 projects on a recommendation from a blogger and it seemed to work well there-- but, no luck here. I think that I am missing something simple, but I do not know what. Can you ZIP a copy of your code to me at mkamoski AT yahoo DOT COM or something or link it here or provide any more hints. Thanks so much. -- Mark Kamoski

  • Anonymous
    December 08, 2010
    dpblogs -- I am stuck. Again. It appears to be running but no data is showing in my ComboBox or the Grid. Where does one set "DataTextField" and "DataValueField", to borrow terms from ASP.NET, for the data binding? I tried setting myComboBox with DisplayMemberPath="CategoryName" but that does not work it seems. Do you have any suggestions? (I apologize for being VERY new to Silverlight; so, I suspect this is someting dirt-simple "once you know how".) Please advise. Thank you. -- Mark Kamoski

  • Anonymous
    December 08, 2010
    The comment has been removed

  • Anonymous
    December 08, 2010
    dpblogs -- OK, I think I got it working. I am pretty sure the orginal had a few lines of code missing or at least some hand-waving there. No issues. Still good. Very helpful. I have posted a new version of the code-infront and the code-behind for my "new" version the Silverlight form here... mkamoski1.wordpress.com/.../self-tracking-entities-in-silverlight-follow-up ...so I really thank you so much because this is EXACTLY the way I want to do this stuff. Thank you. -- Mark Kamoski

  • Anonymous
    December 19, 2010
    hay thanks for nice article , i have one doubt.. i want to send table names dynamically from combobox to wcf so that single service will be able to display data of appropreate table is it possible? [OperationContract]    public List<table name> GetData()    {        // code to return records of table name which is send dynamically        // return table records     }

  • Anonymous
    December 30, 2010
    Abhijit -- Regarding your post above, "i want to send table names dynamically", I know of at least one way that one can do that. It works. It is ugly. It uses code-generation. In short, the design goes as follows. (1) Use t4 code generation to create a static class containing string constants for each table name in your database, (2) change the web method signature to take in the table name, (3) make a big ugly logic switch to determine the proper table to use dynamically, (4) run the Linq, (5) return the data, (6) cast back to the specific type needed in the client (another big ugly logic switch). (6) Etc. This works but it is ugly and it is an issue because we do not get the nice strong-typing of retuning typed STE objects as the article notes above, which is preferable IMHO. Thank you. -- Mark Kamoski

  • Anonymous
    March 30, 2011
    The comment has been removed

  • Anonymous
    June 05, 2011
    I must have missed something. When I add the service reference in the silverlight project, the entity classes is not recognized, despite I have added the entities (.tt file) as link. Instead some proxy classes is generated. What is wrong? Thanks in advance!

  • Anonymous
    July 19, 2011
    Hi, QUOTE: More over EF Self-Tracking template doesn't works when you remove some object from navigation collection of parent object with relation 1-Many. For instance you have Person->Contacts. When you remove contact from "Person.Contacts" collection, apply changes, and try to save changes on objectcontext - Contact object doesn't removed from DB, and when you reload Person.Contacts, you will see removed contact in this collection. END I have exactly the same problem. Does anybody has a solution for that? Thanks!