(or Using NHibernate and WCF)
Introduction
If you have a
Domain Model,
and a rich-client (e.g, WinForms/WPF),
you might want to expose your application's functionality
across a
Service Layer/
Remote Facade.
Your Service Layer will
need to return objects containing domain information, and I prefer to re-use my
Domain Model classes directly, rather than invent unnecessary
DTO
objects (DRY).
Typically your Domain Model might be entities mapped to a relational
database using NHibernate (or some other ORM),
and a Service Layer exposed using WCF (or some other remoting technology).
This should be simple, however there are a few issues you
might run into.
Lazy Load
Most good ORM solutions will use
Lazy Load
to allow the domain
objects to access each other using their associations
while silently taking care of
the data-access concern.
When you come to serialise an object that came from a connected
Domain Model you now have a problem. With Lazy Load it's like pulling
a loose thread that could potentially load up the entire object
model into memory. You need a way to indicate the 'section' of the object
graph that you want.
Lazy Load is also often implemented using
interception,
which in turn usually means that the object
might actually be a generated proxy. Some remoting
technologies do not play well with auto-generated classes;
you might need to transform them back to their 'real' class
to send them across the wire.
(WCF can generate a System.ServiceModel.CommunicationException: ...
Add any types not known statically to the list of known types -
for example, by using the KnownTypeAttribute attribute or by adding
them to the list of known types passed to DataContractSerializer)
In addition, the serialisation of the objects often occurs after the service
call is complete. If the database connection is already closed then you may
be unable to Lazy Load more of the domain.
(NHibernate can generate a NHibernate.LazyInitializationException:
failed to lazily initialize a collection, no session or session was closed)
Domain Object Graph Depth
Some ORM solutions allow you to 'disconnect' a portion of the
object graph to prevent Lazy Load issues, but then you have to handle
two further issues:
1. Loaded Graph is Too Deep ...
During a single service call the domain objects might
load a deep graph to solve a given problem, and then return
a single object as its result. In this case the loaded object
graph might be much larger than you want to send across the wire.
(Either for performance reasons, or security reasons perhaps.)
2. Loaded Graph is Too Shallow ...
Other times the graph will only be partially loaded, and you want
to return a result from a service call that loads deeper information.
For example, a call GetPersonDetail() might need the Person object,
and it's parents, children, addresses, ... or specifically exclude
some personal details. In addition, you need to be careful not
to fall into the
select N+1
problem while fetching all the objects you want.
In both of these cases you again need to indicate the portion of
the object graph that needs to be sent across the wire.
Cyclic Object Graphs
Most connected Domain Models are full of cyclic references,
with bi-direction associations between objects being common.
A typical object-graph for these classes:
public class Person
{
// 1:m association between Person and Child
public IList<Child> Children;
...
public class Child
{
// m:1 association (back pointer)
public Person Parent;
...
... might look like this in memory:
However, if you're going to try exposing your services using SOAP
you're going to find out that cyclic graphs like this are not allowed.
(I can only assume the guys that defined SOAP (WS-I) were asleep the day
they decided that no-one would need to send cyclic graphs across the
wire.)
To send this across the wire
you might need to transform the above graph into something like this:
Solving Using a Graph Utility Class
My preferred option for sorting these problems is to use a utility class that
allows you to quickly specify a graph of objects to 'copy'.
With a sprinkling of Lambda Expressions and extension methods
you can have code that looks like something like this:
// Copies a person, its children (with its parent),
// and the grandchildren.
Person personCopyToReturnAcrossWcf =
myRootPersonObject
.Graph()
.Add(p => p.Children, new Graph<Child>()
.Add(c => c.Parent))
.Add(c => c.Granchildren))
.Copy();
The source for the utility class can be found here:
link
Finally
Some people dislike the idea of sending the 'real' domain objects across the wire.
Instead they create DTO objects for everything that has to be passed back from a service.
My personal preference is to only create DTO classes when they are needed, hopefully saving time
on creating both the DTO objects and the code to map my domain properties to them. Reporting
is an example where this usually breaks down and I find myself creating DTO classes
to send information to a client.
Utimately, whether you use graphing, or use DTOs, you still have to be aware of all of the above issues
when using a Domain Model and a Service Layer/Remote Facade.