Feature CTP Walkthrough: Self Tracking Entities for the Entity Framework (Updated)

The goal of this walkthrough is to demonstrate a basic scenario for the use of the Self-Tracking Entities feature of Entity Framework.

Self-Tracking Entities consist on a set of code generation templates based on Text Template Transformation Toolkit (T4) technology that is used to generate entity types that have the ability to record changes on scalar and complex property values, and on reference and collection navigation properties, independently of the Entity Framework.

The main scenario we are trying to address with Self-Tracking Entities is one in which a WCF service exposes a series of operations that return entity graphs, then a client application can manipulate that graph and submit the modifications to another service operation that validates changes in the graph and then applies them to the database store using Entity Framework.

While the following walkthrough uses C#, versions of the Self-Tracking Entities template exist for both C# and Visual Basic, and all the concepts described apply as well to the Visual Basic version.

Requirements

  1. This walkthrough requires Visual Studio 2010 Beta 2.
  2. The Microsoft Entity Framework Feature CTP 2 that is described here.
  3. A local SQL Server 2008 Express instance has to be installed as SQLEXPRESS. Otherwise, changes to the connection string in the web.config file or regenerating the database from the included SQL scripts may be necessary.
  4. Download and extract the initial solution attached to this post.

Walkthrough

For the sake of brevity we are going to start with a prebuilt Visual Studio solution. The solution is a simple WCF service for Blogging, and contains three projects:

  1. BloggingModel: this is a class library project containing an entity model created using the EDM designer.
  2. BloggingService: this is a WCF service, contains the definition of the service contract and some code for the service.
  3. BloggingConsole: this is a simple console application structured as a series of simple tests for the service.

To start, open the EDM model named Blogging.edmx that is included in the BlogginModel project.

Untitled

Adding the templates

The first step will be adding a T4 template for generating the self-tracking entities. In order to do that, from any blank area in the surface of the EDM designer, right click and select the “Add Code Generation Item…”

 

Untitled

This will display a standard “Add New Item” dialog. Select the item type “ADO.NET Self-Tracking Entity Generator”, specify “Blogging.tt” as the name, and click on Add.

Untitled

Note: As soon as the template files are added to the project, a security warning may pop up asking you to accept only if you trust the source of the template. Accept in order to continue with this walkthrough.

Notice that the BlogginModel project now contains two TT files: Blogging.Types.tt and Blogging.Context.tt.

What has just happened? When you add T4 templates for code generation like this, the default code generation used for Entity Framework models is turned off and the T4 templates take over that responsibility. T4 is a powerful code-generation tool included in Visual Studio. T4 templates can be modified by users to produce different code patterns based on the same input. To learn more about T4, you can visit this link.

Entity types

While self-tracking entities code is generated using the same mechanisms as the default code generation, it takes advantage of the new support for Persistence Ignorance in Entity Framework, and therefore the code generated does not contain any attribute or type defined by Entity Framework. Thanks to this, entities generated by Self-Tracking Entities Template can be used in Silverlight 3. We will describe how to use self-tracking entities in Silverlight in an upcoming walkthrough.

You can browse the Person.cs file under Blogging.Types.tt to get an idea of what it involves.

Untitled

A few things to notice in this code:

  1. There is a DataContract attribute with IsReference = true on each entity type, and all public properties are marked as DataMember. This allows WCF to serialize bidirectional graphs with cycles.
  2. TrackableCollection is a collection type based on ObservableCollection that is also included in the generated code and has the ability to notify each individual change produced in the collection. Self-tracking entities use this type for collection navigation properties. The notification is used for change tracking purposes but also to align various elements that represent the same relationship when any of them changes. For instance, when a Blog is added to a Person’s Blogs collection, the Blog’s Owner reference is also updated to point to the right Person, and the OwnerID foreign key property is updated with the ID of the Owner.
  3. The ChangeTracker property provides access to the ObjectChangeTracker class that stores and controls change tracking information for the entity.

To make it possible to obtain instances of self-tracking entities on the client, we are going to share the code for the types themselves between the service and the client.

Note: Self-tracking entities are not well suited for applications in which sharing the entity types between the client and the server is not possible or practical, like cases in which you only control either the client or the service, but not both. When developing applications for distributed environments in which sharing the types is not possible, consider a solution based on ADO.NET Data Services or custom Data Transfer Objects. Entity Framework’s API can be used to build custom N-Tier solutions. For more information, see here .

As part of this walkthrough we are going to move the code generated for entity types (under Blogging.Types.tt) and the code that is part of the persistence layer (under Blogging.Context.tt) into separate assemblies.

As a first step, we are going to turn off code generation for the templates inside BloggingModel: select the Blogging.Types.tt file on the Solution Explorer. You should now be able to see a property named “Custom Tool” in the Properties window (press F4 if the Properties window is not visible).

 

Untitled

Remove the value “TextTemplatingFileGenerator” and leave it blank. Also, notice that some generated code files under this template are not removed automatically. You will need to expand the tree of generated files under the template and delete them.

Untitled

Then, repeat the operation with Blogging.Context.tt: remove the Custom Tool value and manually delete any remaining generated files underneath.

We are now going to need a new Windows Class Library project to accommodate the entity types. Right click on the solution and add a new class library project using Visual Studio and name it BloggingEntities.

Untitled

Note: You can remove the default Class1.cs file that is automatically added to the project. We are not going to use it in this walkthrough.

Next, we are going to add the template for types to the new BloggingEntities project: right click on the BloggingEntities project and select “Add | Existing Item…”.

Browse for files in the BloggingModel project (you may need to go one level up to see it, and then specify All Files (*.*) as the filter).

Untitled

 

 

 

Select Blogging.Types.tt in the dialog, and on the drop down button at the bottom of the dialog, select the option “Add As Link”.

Untitled

When you add an item to a project as a link, the actual contents of the item are maintained in the location indicated by the original project. This is important because by default the TT file needs to be located alongside an EDMX file in order to obtain all its metadata. However, in the case of T4 templates, links to generated code files are also added to the current project and their contents compiled to the output assembly as well. Since the link is specified using relative paths, the ability to locate the EDMX file is preserved if you move the solution to another directory or in source control.

Note: there are two main ways to move or share the code generated by an Entity Framework T4 template to a different project:

  1. Remove the custom tool in the original location and then add a link to the template in a different project.
  2. Move the template to a different project and then edit the template file to specify a relative path to locate the EDMX file.

In this walkthrough we are going to use the first method.

If you try compiling the BloggingEntities project now, you will see a big number of errors. The reason is that self-tracking entities use DataContract attributes defined in the WCF libraries, which are not referenced by default. Add a reference to the System.Runtime.Serialization library as shown below:

Untitled

Service project

The initial version of the service project contains the service interface definition and basic code for the service implementation. We will be completing and removing commented lines from different parts of the source code until we have a functional WCF service.

Untitled

The three first things the service is going to need are:

  1. The EDM metadata for the Blogging model.
  2. The CLR types for the entities.
  3. The code for the persistence layer (for the sake of brevity we are not going to create a separate assembly for the persistence layer, nor a full blown Repository abstraction).

Metadata

Since the BloggingModel project contains the EDMX file, the output assembly will by default contain the necessary metadata as embedded resources. In order to make the metadata available to the service, we can simply add a project reference on BloggingService pointing to BloggingModel.Untitled

For the Entity Framework runtime to find the metadata, it is necessary to provide the location (in this case a path that points to the embedded resources) in the connection string.

An appropriate connection string is already included in the service’s web.config file, so you won’t need to add it:

 <connectionStrings>
<add name="BloggingContainer" connectionString=
"metadata=res://*/Blogging.csdl|res://*/Blogging.ssdl|res://*/Blogging.msl; provider=System.Data.SqlClient;provider connection string=&quot; 
Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Blogging.mdf; 
Integrated Security=True;User Instance=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>

Entity Types

As explained previously, we are going to share the entity types between the service and the client. We do that by adding a reference to the BloggingEntities class library project.

Untitled

Persistence layer

The persistence layer is going to be composed of the derived ObjectContext type generated by the Blogging.Context.tt file. Add a link to Blogging.Context.tt on the BloggingService project following the same procedure we used previously to add a link to the Blogging.Types.tt template to the BloggingEntities project.

Untitled

 

As before, remember to add it as a link:

Untitled

Important: In order to get the namespace of the code generated by this template to match the namespace of the code generated by the Blogging.Types.tt template, we will need to set the value of its “Custom Tool Namespace” property to “BloggingEntities”.

Untitled

 

Service interface

The service interface is contained in the IBlogging.cs file. The interface defines four methods:

  1. Post GetPostWithComments(int PostID): retrieves a Post object by its ID, including all the Comments related to that post.
  2. Void UpdatePostWithComments(Post post): provides the capability of persisting changes to a particular post, including a collection of its related Comment objects.
  3. GetATestPostID: retrieves an arbitrary PostID existing in the database for testing purposes.
  4. GetATestPersonID: retrieves an arbitrary PersonID from the database for testing purposes.

To continue with the walkthrough, un-comment the code contained in IBlogging.cs.

Service implementation

The service implementation is contained in the Blogging.svc.cs file. GetPostWithComments, GetATestPostID and GetATestPersonID are just regular methods that return results of a query through the service operations.

The only thing that is new about these methods with respect to anything you could previously do with Entity Framework is that they return graphs of self-tracking entities instead of regular entities.

We are now going to focus on the most interesting operation, UpdatePostWithComments, which leverages self-tracking entities in order to save changes that have happened to the object graph on the client.

In order to proceed, uncomment all the commented code in Blogging.svc file.

Update method

This method takes a self-tracking Post entity, passes it through to validation logic, and then applies the changes contained in the object graph to the ObjectContext. Finally, it tries to save the changes to the database.

 public void UpdatePostWithComments(Post post)
{
    ValidateUpdate(post);
    ApplyDefaults(post);
    using (var context = new BloggingContainer())
    {
        context.Entries.ApplyChanges(post);
        context.SaveChanges();
    }
}
Validation

You should always validate the input of the update method. For that reason, the sample in this walkthrough demonstrates how to perform some basic validation that restricts the kind of changes that the self-tracking entities can perform.

 private static void ValidateUpdate(Post post)
{
    ValidateCondition(
        post.ChangeTracker.State != ObjectState.Deleted,
        "Deleting a post is not allowed");
    ValidateCondition(
        post.ChangeTracker.State != ObjectState.Added,
        "Adding a post is not allowed");
    ValidateCondition(
        post.ChangeTracker.State == ObjectState.Unchanged || 
        !string.IsNullOrEmpty(post.Body),
        "Empty posts are not allowed");
    ValidateCondition(
        post.ChangeTracker.State == ObjectState.Unchanged || 
        !string.IsNullOrEmpty(post.Title) ,
        "Empty posts titles are not allowed");
    ValidateCondition(
        !post.Comments.Any(c => c.ChangeTracker.State != ObjectState.Unchanged && 
        string.IsNullOrEmpty(c.Body)),
        "Empty comments are not allowed");
    ValidateCondition(
        !post.Comments.Any(c => c.ChangeTracker.State != ObjectState.Unchanged && 
        string.IsNullOrEmpty(c.Title) ),
        "Empty comment titles are not allowed");
}

 

Essentially, this method will throw an exception if any of the following conditions occur:

  1. The blog post contains an empty body or title.
  2. Any of the comments contain an empty body or title.
  3. The client is trying to add a new post or delete an existing post (the UpdatePostWithComments operation is constrained to only allow updates on posts).
Defaults

The next method invoked by UpdatePostWithComments is ApplyDefaults, which sets the ModifiedDate and CreatedDate on Posts and Comments as appropriate. This kind of method is useful if you expect client code not to do that part of the work.

 private void ApplyDefaults(Post post)
{
    var now = DateTime.Now;
    post.ModifiedDate = now;
    foreach (var comment in post.Comments)
    {
        switch (comment.ChangeTracker.State)
        {
            case ObjectState.Added:
                comment.CreatedDate = now;
                comment.ModifiedDate = now;
                break;
            case ObjectState.Modified:
                comment.ModifiedDate = now;
                break;
        }
    }
}
ApplyChanges

At the core of UpdatePostWithComments, we are using the ApplyChanges extension method that has been generated into the Blogging.Context.Extensions.cs file.

The goal of ApplyChanges is to examine the change tracking information contained in the graph of self-tracking entities to infer the set of operations that need to be performed to reflect the changes in the database.

Basic console client

Included in the solution, there is a very simple console application that is used to test operations on the service. The BloggingConsole project will need two things to operate:

  1. CLR types for entities.
  2. A service reference to BloggingService.

It is worth mentioning that the client doesn’t need EDM metadata at runtime: most of the behaviors that Entity Framework would regularly derive from the metadata are already hardcoded in the entity types.

Adding the entity types

The same way we did it on BloggingService, we can just add a project reference to BloggingEntities in order to get the definition of the types on the client.

Untitled

Adding the service reference

Important: Before you can actually add the service reference, you will need to make sure the service project is built. Otherwise, WCF design-time tools will fail to obtain the service’s metadata. Right click on the BloggingService project entry in the Solution explorer and choose the Build menu item.

Once built, you can proceed to add a standard Service Reference to BloggingService on the BloggingConsole project:

Untitled

When configuring the service reference, specify BloggingService as the namespace. You will need to use the Discover button to find the service exposed by the BloggingService in the same solution.

Untitled

 

 

Since the types of the entities are already known, WCF design-time tools will only add the minimum code necessary for the service interface and for the service client.

Self-Tracking Entities extension methods

Before we proceed to look at the usage of self-tracking entities on the client tier, it is opportune to review some of the ways you can control self-tracking entities. The following are extension methods can be applied to any self-tracking entity:

  • StartTracking: instructs the change tracker on the entity to start recording any change applied to the entity. This includes changes on scalar properties, changes on collections and references to other entities. It is usually unnecessary to invoke this method on the client tier, because self-tracking entities start tracking automatically when they are de-serialized into the client through WCF. An entity that is directly created in code (i.e. using “new”) on the client won’t track changes until either StartTracking is invoked or it is connected through a navigation property to another self-tracking entity that is already tracking changes.
  • StopTracking: instructs the change tracker on the entity to stop recording changes. This method comes handy when it is necessary to manipulate the graph of self-tracking entities without causing changes to be later persisted to the database. For instance, to simulate an “attach” operation on a collection it is possible to stop tracking changes on both end, the call the Add method on the collection, and the resume tracking on both ends with StartTracking:
 post.StopTracking();

comment.StopTracking();

post.Orders.Add(comment);

post.StartTracking();

comment.StartTracking();
  • MarkAs[State]: Modifying the value of a property on a tracking entity will put it in the modified state, but some operations require explicitly changing the state of an entity. For this purpose, four convenience extension methods facilitate changing the state of an entity explicitly to Added, Modified, Deleted or Unchanged. The default state for a newly created entity is added. The MarkAs[State] methods return the same entity they are applied to, with the new state applied to it, therefore facilitating the use of the methods inline:
 post.Author = new Person { ID = authorID }.MarkAsUnchanged();
  • AcceptChanges: this method clears the change tracking information for an entity and moves its state to Unchanged.

Testing methods on the client

In order to proceed with the walkthrough now, uncomment the methods on the file Progam.cs, which contains some simple scenario tests for the service. The program follows this sequence:

  1. An existing PostID and a PersonID are obtained from the service.
  2. Retrieve the post corresponding with PostID.
  3. Add a new comment to the post, and save it through the UpdatePostWithComments service operation. The author for the comment is the person corresponding to PersonID.
  4. Delete all comments for the post and persist this change through the UpdatePostWithComments.
  5. Also, the post is retrieved and displayed in the console at various stages.

Notice that each separate method creates and disposes its own instance of the service client, just for the purpose of testing operations independently.

Executing the program

You can learn a lot more about self-tracking entities while stepping through the Console program and by making adjustments to the code. Make sure you right click on the BloggingConsole project entry on the Solution Explorer and then choosing the “Set as StartUp Project” menu item.

Here is the default output of the program when you execute it:

 

Untitled

Adding comments

One of the most interesting methods here is AddComment:

 private static void AddComment(int postID, int authorID, string title, string body)
{
    using (var service = new BloggingClient())
    {
        var post = service.GetPostWithComments(postID);
        var author = new Person { ID = authorID }.MarkAsUnchanged();
        post.Comments.Add(new Comment { Author = author, Title = title, Body = body });
        service.UpdatePostWithComments(post);
    }
}

The method follows this sequence:

  1. Creates an instance of the service client within a using statement.
  2. Retrieves a blog post from the service using GetPostWithComments.
  3. Create a new Person entity called author. This entity is what is often called a “stub”, since it is only going to represent the identity of a Person that already exists in the database.
  4. Add a new Comment to the Post, by filling the author, title, and body.
  5. Send the proposed change to the service through UpdatePostWithComments.

Note: Newly created self-tracking entities are marked as Added by default. The stub entity of type Person used in this method is moved to the Unchanged state explicitly with the MarkAsUnchanged extension method. Unless we do this, when ApplyChanges back on the service will specify that this is a new Person that needs to be added to the database. Then, SaveChanges will fail while trying to insert a row with a duplicate key.

Using Foreign Keys

The use of a stub entity described above is a common pattern used in Object/Relational Mappers. Since Entity Framework now supports foreign keys, there is an alternative way to relate the Comment with its Author: provided we know the primary key value of the Person that we want to assign as an author, we can set the value to the AuthorID property in the Comment. Using foreign keys the method above would change to this:

 private static void AddComment(int postID, int authorID, string title, string body)
{
    using (var service = new BloggingClient())
    {
        var post = service.GetPostWithComments(postID);
//      var author = new Person { ID = authorID }.MarkAsUnchanged();
        post.Comments.Add(new Comment { AuthorID = authorID, Title = title, Body = body });
        service.UpdatePostWithComments(post);
    }
}

 

With foreign keys, stub entities become unnecessary. In any case, self-tracking entities can use both foreign key associations and independent associations as they appear in the model.

Deleting comments

Another interesting method to look at is DeleteAllComments:

 private static void DeleteAllComments(int postID)
{
    using (var service = new BloggingClient())
    {
        var post = service.GetPostWithComments(postID);
        foreach (var comments in post.Comments.ToArray())
        {
            comments.MarkAsDeleted();
        }
        service.UpdatePostWithComments(post);
    }
}

The method follows this sequence:

  1. Creates an instance of the service client within a using statement.
  2. Retrieves a blog post from the service using GetPostWithComments.
  3. Marks each comment for the post as Deleted.

Note: the foreach loop is performed over a copy of the original collection of comments. This has to be the case because MarkAsDeleted will remove the entity from the collection, and modifying the enumeration over which foreach is looping would cause an exception.

Testing validation

If you want to test how the server-side validation behaves, you can try passing an empty string for title or the body of a comment, by adding a line like this:

 AddComment(postID, authorID, "Bad post", "");

 

Summary

The walkthrough has described basic usage of the Self-Tracking Entities feature. We hope you have learned enough about how to use self-tracking entities that you can experiment with the Feature CTP 2 version on your own code.

There are some areas in which we expect to be improving self-tracking entities in the final release of Visual Studio 2010. Please let us know of any issues you find.

Additional notes

Why not POCO Proxies

Alongside POCO support, we added the capability to create dynamic proxies for entities that comply with certain requirements. Self-tracking entities do not make use of this feature for several reasons:

1. Self-tracking entities are optimized for serialization scenarios, and dynamic proxy types complicate WCF serialization. Before serializing, we would need to take advantage of WCF interception mechanisms to “convert” the instance to serialize back to the original POCO types.

2. One of the characteristics of POCO Proxies is their ability to perform lazy loading. Using serialization and lazy loading together requires much care, since lazy loading may cause unintended data to be queried and included in the graph.

3. In the main scenario we are trying to address in this release, most changes happen on the client side. While change tracking proxies optimize change tracking performance, that only happens when the entities are being tracked by the ObjectStateManager. The default snapshot-based change tracking mechanism used for pure POCO objects provides enough functionality for self-tracking entities.

Database generation scripts

If you need to create the database outside the project, you will find database creation scripts in the App_Data folder of the BloggingService project.

 

EFFeatureCTP2_Walkthrough_STE.ZIP