IT:AD:EF:HowTo:Self Tracking Entities over WCF
Summary
EF has the ability to use EF Entities, POCO or Self-Tracking, all up and down the stack. Up to that point, everything is cherry.
And then you get to the issue of round tripping them to a mobile app or other client…
My first recommendation is …don't. It's a pain, but map the entities to a POCO to pass across the wire (anything else, in an app that you plan to grow over time is basically doomed to pain due to serialization issues with clients you cant recall/replace easily…)
That said, sometimes there is a need. Here's how.
Process
Here’s how it’s done.
Create a WCF app to host the *.svc service endpoints:
IN the app, we want to connect to a db, and make a model from it, by choosing
Add/New Item… in order to select Visual C#/Data/ADO.NET Entity Data Model:
…and follow the prompts to get out on the other side:
THe model I am using is the following simple little thing – but it’s enough to show the gist of what we are after:
Ok. So we have an assembly, and a model.
But we’re not satisfied… The problem is that the Entities inherit from EF4 classes….not POCO in the least. To solve that, we have to click on the Model, in order to select another means of Code Generation:
IN order to select a more appropriate template:
The result will be two T4 templates, under which are the Context, and the Entities:
Here’s where it get’s interesting.
Self Tracking Entities (STE’s) are POCO’s with a little extra bit of code that allows change tracking to happen across the wire. Nice.
But there are limitations to it. It only works across the wire, if the ClientSide is sharing the same Entity assembly.
WHat this means in practices is that the automatically generated Entites have to be moved out the server assembly to an assembly that can be shared between Server and Client.
That is done as follows…
First we have to add a new assembly:
Next, cut/paste the ModelSTE.tt file from the Server assembly to this new shared assembly:
Easy right? Unfortunately, If you right click the newly moved ModelSTE.tt to regenerate the Entities…
…you’ll find out that things stopped working.
The reason is that the *.tt is looking for the original *.edmx file – that is not visible to it.
To solve the problem, edit the modelSTE.tt file to adjust the path back to the original *.edmx:
Now when you right click the *.tt file to Run the Custom Tool:
The model is successfully generated again.
Actually, not yet – you still need to add a reference to System.Runtime.Serialization – but once you’ve done that, it will…
At first glance it may appear that the Entities Assembly has to be .NET 4.0. It doesn’t. Change it to .NET 3.5 – and it will fail. But add a reference to WindowsBase and all will compile:
One other thing. You’ll find out that although the Entities are building – the Context might not be any longer. The reason is that you have to ensure that the namespace used for both match…in the Properties of both *.tt’s ensure that they match up (eg: MyApp.Domain):
Right. Moving right along…
Now let’s add some actual services. First the contract:
using System.ServiceModel; using XAct.Spikes.EFOverWCFUsingSTE.Entities;
namespace XAct.Spikes.EF4OverWCFUsingSTE
{
[ServiceContract]
public interface IService1
{
[OperationContract]
Contact GetContact(int contactId);
[OperationContract]
bool UpdateContact(Contact contact);
}
}
and then an implementation:
using System.Linq;
using XAct.Spikes.EFOverWCFUsingSTE.Entities;
namespace XAct.Spikes.EF4OverWCFUsingSTE
{
public class Service1 : IService1
{
public Contact GetContact(int contactId)
{
using (ModelSTE model1Container = new ModelSTE())
{
return model1Container
.Contacts
.Include("Addresses")
.Where(c => c.Id == contactId).Single();
}
}
public bool UpdateContact(Contact contact)
{
using (ModelSTE model1Container = new ModelSTE())
{
model1Container.Contacts.ApplyChanges(contact);
return (model1Container.SaveChanges() > 0);
}
}
}
}
which means your solution now looks more like this:
Compile, Run once (to ensure it’s running in Cassini for the next stage), and then we create a new assembly. This time an MSTest Project , with a Service Reference back to the above running service:
in Which we add a Service Reference:
Great. But watch out. While building it, if you want the self-tracking to work over WCF, you have to remember to a) Add a ref to the entities assembly first, and then ensure its used when generating the Proxies:
If you don’t have this set, you won’t get the self tracking capabilities you are expecting.
In fact, for this this demo, I want you to specifically uncheck it as follows:
Go ahead and create the proxies…
As for our test, we create a test as follows:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestProject1.ServiceReference1;
namespace TestProject1
{
/// <summary>
/// Summary description for UnitTest1
/// </summary>
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
using (Service1Client service2Client = new Service1Client())
{
Contact contact = service2Client.GetContact(1);
if (contact.Addresses.Count > 0)
{
contact.Addresses[0].Street =
string.Format("A STE modified Street:{0}", DateTime.Now);
//We'll get back to this in a second...
//contact.Addresses[0].ChangeTracker.State = ObjectState.Modified;
}
contact.Addresses.Add(
new Address
{
Street = string.Format("Newly Added Street:{0}", DateTime.Now)
});
service2Client.UpdateContact(contact);
}
}
}
}
Run that test :
and then check your db…you’ll see the following:
Notice that the first address did NOT update as expected.
The reason is that we are not sharing the same dll across both ends of the wire.
We can get around that by going back to our test code and unchecking the commented out lines:
contact.Addresses[0].ChangeTracker.State = ObjectState.Modified;
to mark that we have done changes, and that would do the trick nicely. THis time we our db would show:
But the whole point of STE’s is to take care of all that for you…so let’s go back and fix that. To do that, you have to go back to configure the way the webservice proxies are generated:
and this time ensure that the proxies are generated using the shared entities. And don’t forget to comment back out the following or you won’t get a clean read of how things work automatically now:
//contact.Addresses[0].ChangeTracker.State = ObjectState.Modified;
Try again…(I just deleted all the rows in the db as well btw). This time, you’ll see:
Tada! Doesn’t look like much – but you just got automatic tracking happening over the wire. Nice. Very nice.
What I like about it is that it solves two problems with one stone.
a) we can use the STE on the Server Side with all the advantages of not having to worry about whether we forgot flags or not b) we can expose the same services/entities to non-.NET clients – they’ll just have to be a little bit more careful, and set the ChangeTracker.State manually.
Compared to POCO, I find that STE’s give me a wider breath of use (POCO’s can have issues being serialized) at the price of having to give up lazy loading. I find that a perfectly acceptable route, as Lazy loading, in anything but the most trivial of apps is highly suspect. Certainly has no place over the wire.























