Developing with today’s platforms, a developer can utilize great tools and libraries to increase development speed and software quality. Two very common in the .NET space are WCF and LINQ to SQL, besides various other O/RM tools like NHibernate and the ADO.NET Entity Framework.
Unfortunately, using a messaging framework like WCF that has to perform quite a lot serialization work in combination with a tool to automatically retrieve data from data sources and create deeply nested object graphs like LINQ to SQL creates some pretty hard challenges. The reason for this is that WCF has to transform the graph into a “flat” message representation. If the graph contains cyclic references, WCF has two options to avoid running in circles (and kill your App) when it encounters an object it already has processed:
- Abort the whole serialization. This is the default behavior and it is very reasonable to throw an exception here.
- “Mark” that part of the message as “reference” to an earlier serialized part. This behavior can be introduced using your own WCF components (see the links at the bottom).
For service developers that want to use object-relational mapping tools to automate database queries, neither of one is really sufficient. Although the latter part may be an option in a strict .NET to .NET scenario, it does not implement any standard (because there is none) and thereby makes it nearly impossible for other platforms to use the serialized message with reasonable effort.
Even worse, in the Web 2.0 area more and more WCF services create JSON and XML output for client-side components like ASP.NET AJAX, JQuery, Silverlight and Flex. Therefore it is essential to provide client-side developers with all necessary information in a strctured way without the need to blow up the server side development.
Taking a look at the .NET framework, Microsoft currently does not offer any solution for this in its primary frameworks WCF and LINQ.
After re-designing and overworking our Reference Architecture over the last week, we finally managed to develop an approach here at netzkern to integrate both technologies with each other in a pragmatic and pretty “clean” way. We created a utility called the DataTransferObjectManager that is able to dynamically fill DTOs with the values and copies of all requested references. The trick here is that it does only selectively chooses the right references by letting the client-side developer provide a configuration parameter that indicates which properties he really needs to do his work. All other references are ignored and not returned to the client, they are usually not even loaded from the database.
Let’s have a look. First, we create two entity classes as part of our model. They don’t need to be public as they are never used outside of the core assembly.
namespace App.Core
{
class House
{
public string Name { get; set; }
public Person Owner { get; set; }
}
class Person
{
public string Name { get; set; }
public House Residence { get; set; }
}
}
Let’s pretend that instances of these classes are being tracked by an O/RM manager component that automatically detects changes and performs updates. Now, let’s create the corresponding DTO’s:
namespace App.DTO
{
public class HouseDTO
{
public string Name { get; set; }
public PersonDTO Owner { get; set; }
}
public class PersonDTO
{
public string Name { get; set; }
public HouseDTO Residence { get; set; }
}
}
These classes are basically message definitions. In WCF you would use these as DataContracts and annotate them with the appropriate attributes. Instances of these classes are somewhat “dumb”, they only contain data.
Now let’s consider, we have the following object graph:
House h1 = new House();
h1.Name = “House No. 1”;
Person p1 = new Person();
p1.Name = “Person A”;
h1.Owner = p1;
p1.Residence = h1;
This creates a graph with just two objects and a cyclic reference. When you try to serialize “h1” using WCF’s default DataContractSerializer, you get the above mentioned exception. Now let’s use the netzkern.DataTransferObjectManager (or nk.DTOM for short) and see what it can do for us:
// Create an empty base DTO.
HouseDTO h2 = new HouseDTO();
// Call the nk.DTOM and copy the contents of h1 to h2.
DataTransferObjectManager.CreateDataTransferObject(h1, h2);
What we get from this is the following “filled” DTO message object:
h2.Name == “House No. 1”;
h2.Owner == null;
So far we created a flat copy of “h1” with all references excluded. Now let’s include a copy of “h1.Owner” as part of “h2”:
// Create an empty base DTO.
HouseDTO h2 = new HouseDTO();
// Tell the nk.DTOM to include the following properties.
PropertyPathCollection includePaths = new PropertyPathCollection();
includePaths.Add(“Owner”);
// Call the nk.DTOM and copy the contents of h1 to h2.
DataTransferObjectManager.CreateDataTransferObject(h1, h2);
Our result now includes a copy of “p1” that has been mapped to a PersonDTO type:
h2.Name == “House No. 1”;
h2.Owner == [PersonDTO];
h2.Owner.Name == “Person A”;
h2.Owner.Residence == null;
We can now even include “deeper” properties, that are being copied equally by the nk.DTOM:
// Create an empty base DTO.
HouseDTO h2 = new HouseDTO();
// Tell the nk.DTOM to include the following properties.
PropertyPathCollection includePaths = new PropertyPathCollection();
includePaths.Add(“Owner.Residence”);
// Call the nk.DTOM and copy the contents of h1 to h2.
DataTransferObjectManager.CreateDataTransferObject(h1, h2);
That creates the following result:
h2.Name == “House No. 1”;
h2.Owner == [PersonDTO];
h2.Owner.Name == “Person A”;
h2.Owner.Residence == [HouseDTO];
h2.Owner.Residence.Name = “House No. 1”;
h2.Owner.Residence.Owner = null;
So, how do we use this in WCF? After you have successfully loaded entities in your service operation using an O/RM, you just call the nk.DTOM and return the DataTransferObject as result. The trick is to let the client(!) choose which parts of your internal object graph are necessary to do its work. A simple service call might look like this:
IHouseService service = new HouseServiceClient();
HouseRequest req = new HouseRequest();
req.HouseID = houseID;
req.IncludeProperties = “Owner.Residence”;
HouseResponse resp = service.GetHouse(req);
That’s basically it. By using the netzkern.DataTransferObjectManager, you can define your data transfer objects independently of your internal entity objects. You can even omit certain properties and nk.DTOM will ignore them. We will now test this component internally and make it available on CodePlex as soon as it reaches BETA status. We also would love to hear your thoughts, just send a mail to blog@juliusganns.com.
