Aspen – A sample app using Silverlight 4 and .Net 4.0 – part 4 of X – DomainService, using DTO and Unity
Note: The code in this post is based on the VS 2010 Beta 2, and changes can be made until it hits RTM.
In my previous post I wrote about how I have implemented the MemberRepository and prepare it for using Dependency injection of an ObjectContext etc. Since the last post I have created a WCF RIA Services DomainService for returning a list of members. I decided to use DTO (Data Transfer Object) in this application even if the application is quite simple. This app will be used as an reference app, and most applications and blog post is about passing DAL types directly from the DomainService. There is nothing wrong with passing DAL types directly to the client if the app is a small app or has few users. I decided to use Unity 1.2 from Microsoft Patterns & Practices as the IoC Container, so this post will also show how I have used the Unity to inject dependencies into the a DomainService.
About DTO
Using DTO or not is only based on the business case. If our application will not take the benefits from using DTO, don’t use it. There is no Silver bullet, and applications will not look the same, have that in mind.
“My First Law
of Distributed Object Design: Don't distribute your
objects”
– Martin Fowler (P of EAA)
“Objects have been around for a while, and sometimes
it seems that ever since they were created, folks have
wanted to distribute them. However, distribution of
objects, or indeed of anything else, has a lot more
pitfalls than many people realize, especially when they’re
under the influence of vendors’ cozy brochures.”
– Martin Fowler (http://www.ddj.com/architect/184414966)
Instead of sharing a single entity implementation
between the mid-tier and the client, we can create a custom
object (DTO) that is only used for passing data over the
wire. Most entities in our domain model is not often
designed with distribution in mind. Using DTO have two main
benefits regarding to Daniel Simmons (Architect on the
Entity Framework team at Microsoft):
“It isolates your service contract from
implementation issues on the mid-tier and the client,
allowing that contract to remain stable even if the
implementation on the tiers changes, and it allows you to
control what data flows over the wire. Therefore, you can
avoid sending unnecessary data (or data the client is not
allowed to access) or reshape the data to make it more
convenient for the service. Generally, the service
contract is designed with the client scenarios in mind so
that the data can be reshaped between the mid-tier
entities and the DTOs (maybe by combining multiple
entities into one DTO and skipping properties not needed
on the client), while the DTOs can be used directly on the
client.
These benefits, however, come at the price of having to create and maintain one or two more layers of objects and mapping.“ – Daniel Simmons (Building N-Tier Apps with EF4)
Implementation of the WCF RIA Services DomainService
When I implemented the DomainService to retrieve
members I created a “WCF RIA Services Class Library” to
separate the DomainService from the Web project hosting the
Silvelright application. The DomainService I have created
will use a DTO for transferring data from the server to the
client.
Note: In the current stage of the “Aspen” project the
the Member entity in my domain model isn’t completed, so
the DTO will at the moment look the same as my domain
entity.
Here is the implementation of the DTO:
public class MemberDto { [Key] public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } }
WCF RIA Services require us to specify a key for the
Entity passed over the wire, so that’s the reason why the
KeyAttribute is added. For me this will sort of make the
MemberDto not a pure POCO. But there is or will probably be
a way with WCF RIA Services to specify the Keys and other
attributes with a Fluent-API or with XML so we can use pure
POCOs:
http://www.nikhilk.net/RIA-Services-Fluent-Metadata-API.aspx.
I have created a simple DomainService for the
MemberDto, MemberDomainService. The MemberDomainService at
the moment will only return a list of MemberDto. The
MemberDomainService is only a Service Layer and will not
contain any kind of business logic, the business logic is
added to the domain model. To make it possible to test the
MemberDomainService and remove as much dependencies to
details as much as possible, the MemberDomainService will
work against an abstraction, in this case against the
IMemberRepository. A constructor is added to the
MemberDomainService to take a IMemberRepository as an
argument. Here is the implementation of the
MemberDomainSerivce:
[EnableClientAccess()] public class MemberDomainService : DomainService { readonly IMemberRepository _memberRepository = null; public MemberDomainService(IMemberRepository membershipRepository) { if (membershipRepository == null) throw new ArgumentNullException("membershipRepository"); _memberRepository = membershipRepository; } public IEnumerable<MemberDto> GetMembers() { var members = _memberRepository.GetAll(); return members.Select( m => { return new MemberDto() { ID = m.ID, FirstName = m.FirstName, LastName = m.LastName }; }); } }
The MemberDomainService will at the moment handle the
transformation between the Member entity into the MemberDto,
I haven’t created a helper method for the mapping between
the domain entity and the DTO, maybe I will use the
AutoMapper
to simplify the mapping in the future, I will see.
By default the WCF RIA Services uses a factory to
create a DomainService, the problem is that the default
factory shipped with WCF RIA Services will use a
DomainService default constructor and will not do any
dependency injection. To make sure the MemberRepository will
be injected to the MemberDomainService I need to create a
new DomainService Factory. I decided to simplify the
dependency injection by using Unity 1.2, so I created a new
Class Library called, WcfRisServices.Extension,and added my
Unity DomainService factory to the project. Here is the
implementation of the UnityDomainServiceFactory:
public class UnityDomainServiceFactory : IDomainServiceFactory { private IUnityContainer _unityContainer = null; public UnityDomainServiceFactory(IUnityContainer unityContainer) { if (unityContainer == null) throw new ArgumentNullException("unityContainer"); _unityContainer = unityContainer; } public DomainService CreateDomainService(Type domainServiceType, DomainServiceContext context) { var service = _unityContainer.Resolve(domainServiceType) as DomainService; service.Initialize(context); return service; } public void ReleaseDomainService(DomainService domainService) { domainService.Dispose(); } }
The UnityDomainServiceFactory will take the type
IUnityContainer as a constructor argument. The container
passed into the UnityDomainServiceFactory will contain all
registered types. By using the Resolve method of the
UnityContainer, the Unity will create an instance of the
requested type (in this case the MemberDomainService) and
inject all dependencies to the created type. In this case
the MemberRepository, and when the MemberRepository will be
created by the Unity, it will also inject and instance of
the ObjectContextAdapter and pass a connection string as an
argument to the ObjectContextAdapter’s constructor.
At the moment I decide to make it simple to register
all types to the UnityContainer, so I decided to use the
global.asax’s Application_Start event handler. I also
specify which factory to use when WCF RIA Services will
create the instance of the DomainService.
public static UnityContainer UnityContainer = new UnityContainer(); protected void Application_Start(object sender, EventArgs e) { UnityContainer.RegisterType<MemberDomainService>(); UnityContainer.RegisterType<IMemberRepository, MemberRepository>(); UnityContainer.RegisterType<IObjectContext, ObjectContextAdapter>( new InjectionConstructor(AspenConnectionString)); DomainService.Factory = new UnityDomainServiceFactory(UnityContainer); } private string AspenConnectionString { get { return ConfigurationManager.ConnectionStrings["AspenConnectionString"].ConnectionString; } }
I will later remove the registration of types from the Application_Start, I placed it there temporary to see if Unity will work well in VS2010.
If you want to read more about how to create a DomainService factory, take a look at the following blog posts:
WCF RIA Services Unity DomainServiceFactory
WCF RIA Services – StructureMap – DomainServiceFactory
If you want to know when I publish new blog posts,
then you can follow me on twitter:
http://www.twitter.com/fredrikn