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