Entity Framework v4, End to End Application Strategy (Part 3, Changes to Data Layer)

Initially in my service layer, I was doing all of my serialization with the Json.NET library, for 2 primary reasons
  • Control over how dates get formatting (I don’t like the \/Date(123)\/ MS AJAX style)
  • The ability to serialize object graphs that contain cycles (by telling Json.NET to ignore them)
Unfortunately, this started causing me too many problems with WCF. My service methods couldn’t return DTO types directly — that would automatically cause the use of DataContractJsonSerializer. So initially I just returned JSON strings. But then you end up with stuff like this:
“{\”Message\”:\”Hello World\”}” (double wrapped or double quoted — requires two JSON.parse() calls)
instead of this:
{“Message”:”Hello World”}

For more info regarding that problem, see the StackOverflow question I asked about it. It details how to still use Json.Net, but not have the “double quoted” or “double wrapped” json string. Unfortunately, the solution really only works for sending responses back to the client, and it also forces the use of json, so you can’t have soap endpoints to your service methods. For service methods that take object types as params, you can’t force WCF to use Json.Net to deserialize, and it will automatically use DataContractJsonSerializer. This means if your dates aren’t in MS AJAX date format, you’ll throw exceptions. So I’m ditching Json.Net in this particular app stack. Soon, I want to do an article on creating a fully equipped REST service by creating your own IHttpHandler, instead of using WCF. In that case, I think Json.Net would be the best choice for serialization and deserialization. But that post is for another time.

So, I’m back to using DataContractJsonSerializer. The big advantages to this are:
  • My service methods return DTO types, not the arbitrary “Message” type (System.ServiceModel.Channels.Message), or “double wrapped” json strings.
  • My service methods can take object types (DTO types, entity types, whatever) as input params, as opposed to json strings (personJsonString), which require manual deserialization within the service method
  • My services can now have both REST (json) and SOAP (xml) endpoints
  • With both endpoint types, the service can easily be called either directly from client code, or in code behind, using the proxy classes created with Add Service Reference
But, using DataContractJsonSerializer does bring back the 2 problems I had in the beginning: Dates and Cycles. The MS AJAX style dates I can live with (uhhg). The cycles, however, need to be remedied, or WCF will throw a hissy fit (also known as a runtime exception).

Removing Cycles (Almost) Automatically

Imagine that a Person object has an Address object, and an Address object has a list of Person objects that have that Address.
somePerson.Address; someAddress.People;
This creates a cycle in our object graph.
.
Since json doesn’t support references, cycles would cause the DataContractJsonSerializer to infinitely recurse (since it can’t be simply told to ignore cycles, like a much superior serializer I know about). This just means we have to strip out cycles from our object graphs before we try to serialize them. That shouldn’t be too hard. We’ll start with a root object, and traverse all known related objects, adding each object to a flat list along the way. If we encounter an object that’s already in our running flat list, then we assign parent object’s property to null, or remove it, or assign it to a stub object, just so long as it doesn’t point to the object in question, since it’s already in our graph once. This breaks the cycle. The way I accomplished it is with a set of recursive methods in each of the DTO classes (I’m only interested in stripping cycles from DTO graphs, since that’s all I send to the client).

Here’s a reminder of what our sample model looks like.

Given this model, the code to strip cycles (with Person as the root object) is:
public  partial class PersonDto
{
	public PersonDto StripCycles()
	{
		Dictionary<object, string> graphObjs = new Dictionary<object, string>();
		StripCycles(graphObjs, "");
		return this;
	}

	internal bool StripCycles(Dictionary<object, string> graphObjs, string path)
	{
		if (graphObjs.ContainsKey(this))
			return false;
		graphObjs.Add(this, path);

		// Single navigation property references
		if (Address != null)
			if (!Address.StripCycles(graphObjs, path + ".Address"))
				Address = null;

		// Collections
		if (Projects != null)
			for(int i = 0; i < Projects.Count; ++i)
				if (!Projects[i].StripCycles(graphObjs, path + ".Projects[" + i + "]"))
					Projects.RemoveAt(i--);
		return true;
	}
}

So we’ve got 1 public method which can be used very easily. somePerson.StripCycles(); That can be safely serialized, no matter how crazy your object graph was before. This of course requires that each DTO class also has the same 2 StripCycles() methods implemented. Each implementation is different. Each class will have logic for each navigation property, and each collection of other DTO types. The methods aren’t directly recursive; but they do all call each other. A calls B which calls A which calls B, until your “base case” is reached, which is either you end up at a leaf node of your object graph, or you encounter a cycle, and break it (which is really just turning it into a leaf node), then the calls start popping back up.

Now, since it would be quite tedious to create and maintain these methods for each DTO class (especially during initial development, where new types are often added, and existing ones are constantly modified), we’ll again leverage the T4 templating engine to create these methods for us. We have all the model’s metadata exposed to the T4 runtime, so this is pretty easy. I won’t post the code inline, since the whole template is around 400 lines. But it’s an updated version of the DtoTemplate.tt I previously uploaded. Download the project zip at the end of the post to check out the new templates.

More options when removing cycles

You may not want to just “break” the cycle. You might want some indication that the relationship exists. In the case of the drawing, sam and sam.Address.People[0] refer to the same object in memory. Well, you could assign sam.Address.People[0] to a “stub object” that gives us the info we need (like the primary key value of the real object).
// Break the cycle by replacing the circular reference with a "stub" object
sam.Address.People[0] = new PersonDto { ID = 1 };
This approach does have some additional problems though. In client script, you’ll have objects that only have their PK field populated. And you might have to try to “hunt down” the real object elsewhere in the object graph to get its other properties. Very annoying. So, in the T4 template, I provide some additional options for how cycles will be removed. If the “stub” objects are used, then each DTO type will have some additional properties, shown here.
/// <summary>
/// If true, this is not the original object.  This is a placeholder that only has the ID and FK properties set.
/// The original object was removed, to prevent cycles in the object graph.
/// </summary>
[DataMember] public bool __isCycleStub { get; private set; }

/// <summary>
/// The path to the "real" object that this stub represents, from the root of the original object graph.
/// This can be resolved in javascript like this:
/// 	var realObj = eval("graphRoot" + stubObj.__pathFromRoot);
/// 	alert(realObj.someProperty);
/// </summary>
[DataMember] public string __pathFromRoot { get; private set; }
This makes the stub objects easy to find, and the real objects they represent easy to track down.

Wrapup

All in all, there are quite a few changes I’ve made to the data layer. Also, since this is my foundation strategy for new projects right now, I’ve collected all of the templates, utility classes and extension methods into their own project, so I can easily redistribute them as a group for use in multiple dependent projects. And through the glory of svn:externals, it’s easy for my dependent projects to get the latest version of all these tools. Anyway, rather than post all of the files individually, here’s the whole project:
DataLayerHelpers.zip
And here’s what the project looks like.

The zipped project comes with the latest AutoMapper in the Lib folder, and a custom build of HyperDescriptor that works against .NET 4 (StackOverflow question concerning HyperDescriptor in .NET 4). There are also some handy singleton classes in there. I especially like the PerRequestSingleton, which creates a single instance of a class that is per request (ASP.NET-page-life-cycle-wise), and per user (it uses HttpContext.Current.Items for storage). It’s a great way to make sure you’ve always got an instance of your ObjectContext class on hand, but without having to worry about when and where you create it.

Next time I will try to get to the actual WCF REST service.






This entry was posted in Code and tagged , , , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

3 Comments

  1. Posted July 10, 2010 at 8:16 pm | Permalink

    Amiable brief and this post helped me alot in my college assignement. Gratefulness you on your information.

  2. Posted January 17, 2011 at 8:02 am | Permalink

    Hi,

    First of all – thanks for the info and the great article!
    Second, I do have a question. When running the application I keep getting error related to the mapping. Something like:
    Trying to map HotScores.Entities.Country to HotScores.Entities.Dto.CountryDto.\nMissing type map configuration or unsupported mapping.\nException of type ‘AutoMapper.AutoMapperMappingException’ was thrown.

    Any ideas?

    Thanks,
    Nadav

  3. Posted January 31, 2012 at 4:17 am | Permalink

    Hi,
    Can you post samples for BusinessLogicLayer?

    regards,
    NJ

One Trackback

  1. [...] Sam's Code Programming snippets, observations, questions, and news (.NET, C#, SQL, jQuery, javascript, design patterns, NHibernate, Entity Framework, etc) Skip to content About « Entity Framework v4, End to End Application Strategy (Part 1, Intro) Entity Framework v4, End to End Application Strategy (Part 3, Changes to Data Layer) » [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>