Using Self Tracking Entity to retrieve and update

In this tutorial I will explain how to use Self Tracking Entity to retrieve entities from a WCF service, modify the object graph on the client side and then send those changes back to the server in a clean way.

Suppose we have Resource and Roles with many-to-many relationship as shown below.

image

2. Create a class library project and import the above table structure into an Entity Data Model as shown below.

image

Entity Framework will see the link containing primary keys from both tables and will model it as many-to-many association.

For WCF scenario it is recommended to use STE template for code generation for your entities.  Self tracking entity implements IObjectWithChangeTracker which allows the entities to keep track of its own changes. For instance if you modified scalar properties, reference properties or collections, the change tracker would keep a log of what’s changed.  This is especially useful for WCF clients where there is no concept of an ObjectContext on the client side and we need some mechanism to keep track of the changes client is making. Once those changes are captured, we can send the entities to the server and replay those changes on the ObjectContext using ApplyChanges method.

To generate Self Tracking Entities for your model, right click anywhere on the model, select Add Code Generation Item and choose Self Tracking Entity template. Build the project and make sure there are no errors.

3. Add a new WCF service application and reference the class library project which contains your model and self tracking entities. Add reference to System.Data.Entity.

4. Create a service interface and its implementation with three methods.  First method will return a Resource given a resourcename along with the roles associated to that resource. Second method should return a role give a role name. Last method would take modified object graph for resource and save it to the database. Code below shows the interface and its implementation.

image

image 

In the Service1 class, to save the resource, we use ApplyChanges method to attach the entity. ApplyChanges is a special method which looks a the change track to see what has changed and replays those changes on the object context. In our case we have added an existing role to a resource so we should see a new entry created in REsourceRole table.

5. Create a console application that will serve as a client to our WCF service. Add reference to our class library project that contains our self tracking entities. Next start the instance of the service created earlier so you can add service reference in our client project. It is important to explain at this point that when you add WCF service reference, you will not see entity classes generated on the client side. This is because the service returns entities whose reference is already available when we added reference to the class library project. This means that we are basically sharing the same code for entities on the server and client. If you did not add reference to the class library project and let service reference generate its own classes then you would end up with basic entities which have no ability to keep track of its changes.

On the Program.cs class add the following code which basically retrieves a a given resource, retrieves a role and then adds that role to the resource’s roles collection and sends the changes to the server.

image

The complete structure of my solution contains 3 projects, a class library project containing my model and Self Tracking Entities, a service that exposes my entities and a console application that consumes the serve. Figure below shows the screen shot from Solution explorer

image

5 Comments

  • Hi nice. However is there an easy way of doing this without making the call to the service wtih GetResourceWithRole and GetRole by resolving this server side from "Withdraw Money" and "Supervisor"?

  • Hi all
    Is there any way not to send dependant unchanged objects from the graph to the service? I have sample Customer - Orders from Northwind with one customer having 556 orders. When modifying Customer details and sending to the server, the service will recieve all Orders as well causing ApplyChanges to hand for 15 seconds.
    Any ideas please ?

  • I think u can. What you would need to do is call stoptracking and removing all entities that are unchanged, then call start tracking and send the changes to the server. I cover something similar for fixing duplicate object graph on the client side in my book.

  • This seems to be working. Thanks

  • I built a small sample to show my issue exactly. If I execute two methods, the first one succeeds:

    using (TestEF4M2M.TestEntities context = new TestEF4M2M.TestEntities())
    {
    Employee employee = context.Employees.SingleOrDefault(emp => emp.EmployeeID == 1);
    employee.StartTracking();
    employee.Projects.Add(context.Projects.SingleOrDefault(pr => pr.ProjectId == new Guid("08617439-3AA4-4271-AE6C-110846FB0F9A")));
    employee.Projects.Add(context.Projects.SingleOrDefault(pr => pr.ProjectId == new Guid("9B90220E-E28E-472A-86DA-3FD12F863CFB")));
    Project p1 = employee.Projects.SingleOrDefault(pr => pr.ProjectId == new Guid("08617439-3AA4-4271-AE6C-110846FB0F9A"));
    employee.Projects.Remove(p1);
    context.Employees.ApplyChanges(employee);
    context.SaveChanges();
    }

    However when I execute the second method it fails:

    using (TestEF4M2M.TestEntities context = new TestEF4M2M.TestEntities())
    {
    Employee employee = context.Employees.SingleOrDefault(emp => emp.EmployeeID == 1);
    employee.StartTracking();
    Project p1 = employee.Projects.SingleOrDefault(pr => pr.ProjectId == new Guid("9B90220E-E28E-472A-86DA-3FD12F863CFB"));
    employee.Projects.Remove(p1);
    employee.Projects.Add(context.Projects.SingleOrDefault(pr => pr.ProjectId == new Guid("08617439-3AA4-4271-AE6C-110846FB0F9A")));
    employee.Projects.Add(context.Projects.SingleOrDefault(pr => pr.ProjectId == new Guid("9B90220E-E28E-472A-86DA-3FD12F863CFB")));
    context.Employees.ApplyChanges(employee);
    context.SaveChanges();
    }

    Thank you in advance for your help.

    Evgeny.

Comments have been disabled for this content.