Published: 24 Jan 2011
By: Manning Publications

This article is taken from the book Dependency Injection in .NET. The author discusses WCF’s extensibility points used in DI— IInstanceProvider interface and contract behaviors. The author also shows how to extend a commerce application with a WCF-based service.

Contents [hide]

About the book

This is a sample chapter of the book Dependency Injection in .NET. It has been published with the exclusive permission of Manning.


Written by: Mark Seemann
Pages: 375
Publisher: Manning
ISBN: 9781935182504

Get 40% discount

DotNetSlacker readers can get 40% off the full print book or ebook at www.manning.com using the promo code dns40 at checkout.

Windows Communication Foundation (WCF) is one of the most extensible parts of the Base Class Library (BCL). While it is fairly easy to get started writing WCF services, the myriad of extensibility points can make it hard to find just the one you need. This is also the case when it comes to DI.

Note

A joke claims that WCF is really an acronym for Windows Complication Foundation. There's a certain degree of truth in that claim.

You could easily be led to believe that WCF does not support Constructor Injection. If you implement a WCF service with Constructor Injection and no default constructor, at runtime the WCF service host will throw a ServiceActivationException with a message similar to this:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type or pass an instance of the type to the host.

This message strongly indicates that a default constructor is required. The only way out seems to be to pass an already created instance to the WCF host. However, that raises several issues:

How can we do this if we host the service in Internet Information Server (IIS)?

This requires the service to run in a single InstanceContextMode, which is undesirable for a number of other reasons.

The good news is that the exception message is misleading. There are other ways to enable Constructor Injection with WCF.

WCF extensibility

WCF has lots of extensibility points, but when it comes to DI we only need to know about the IInstanceProvider interface and contract behaviors. A contract behavior is a Seam in WCF that allows us to modify how a given contract (in other words, a service) behaves.

IInstanceProvider is an interface that defines how service instances are created (and released). Here is the interface definition in all its glory:

The two GetInstance overloads are responsible for creating an appropriate service instance, while ReleaseInstance provides a hook for cleaning up, if necessary.

The default implementation simply looks for a default constructor on the service type, but we can replace it with one that uses DI. Figure 1 illustrates the overall flow when a hosted service receives a message.

Figure 1: When a message (request) arrives for service operation, WCF determines which CLR type will implement the service. It asks a ServiceHostFactory to create an appropriate ServiceHost that can host the requested service. The ServiceHost does its part by applying behaviors and creating the requested instance.

When a message (request) arrives for service 

operation, WCF determines which CLR type will implement the service. It asks a ServiceHostFactory to create an appropriate ServiceHost that can host 

the requested service. The ServiceHost does its part by applying behaviors and creating the requested instance.

When you host a WCF service on IIS, a ServiceHostFactory is mandatory, although the default implementation will be used if you don't explicitly define an alternative. If you host the service manually, a ServiceHostFactory may still be useful but is not required because you can create the appropriate ServiceHost directly in code.

When the ServiceHost applies behaviors, it picks them up from at least three different places before aggregating them:

  • Attributes
  • .config file
  • In-memory objects

While we can define behaviors in attributes, it's not a particularly attractive strategy to use when it comes to DI because that means that we are compiling a particular creation strategy with particular Dependencies into the code. The net result is almost the same as if we had simple hard-coded the Dependencies directly in the service, just in a much more convoluted way.

A configuration file may sound like the ultimate in flexibility but really is not because it does not allow us to imperatively configure Dependencies if we should like to do that.

In-memory objects give us the best flexibility because we can choose to create the Dependencies directly in code or based on configuration settings. If we use a DI Container, we get both options for free.

This means that we should create a custom ServiceHostFactory that creates instances of a custom ServiceHost that again can wire up the desired service with all its Dependencies.

You can create a set of general-purpose classes that do this based on your DI Container of choice or use one of the already implemented reusable container-based ServiceHostFactories. You can also create a specialized ServiceHostFactory for a particular service. Because it provides the best illustration of the process, I have chosen a specialized factory for the following example.

Example: wiring up a product management service

As an example, let us imagine that we have been asked to extend a commerce application with a WCF-based service that exposes operations that allow other applications to manage product data. This would allow us to hook up a rich client (we will do that in a subsequent section) or a batch job to manage product data.

Introducing ProductManagementService

To keep the example simple, let us assume that we wish to expose simple Create, Read, Update, and Delete (CRUD) operations. Figure 2 shows a diagram of the service and associated Data Contracts.

Figure 2: The IProductManagementService is a WCF service that defines simple CRUD operations on products. It uses the associated ProductContract and MoneyContract to expose these operations. Although not shown in this diagram, all three types are decorated with the usual WCF attributes: ServiceContract, OperationContract, DataContract, and DataMember.

The 

IProductManagementService is a WCF service that defines simple CRUD operations on products. It uses the associated ProductContract and MoneyContract 

to expose these operations. Although not shown in this diagram, all three types are decorated with the usual WCF attributes: ServiceContract, 

OperationContract, DataContract, and DataMember.

Since we already have an existing domain model, we wish to implement this service by extending the domain model and expose its operations through this WCF contract. The exact details are not important; just note that we expand the abstract ProductRepository class.

The domain model represents a product as the Entity Product, while the service contract exposes its operations in terms of the DTO ProductContract. To map between these two different types, we also introduce an interface called IContractMapper.

Entity vs. DTO

In the previous paragraph I just threw some more jargon at you, but let's briefly review what is meant by Entity and DTO.

An Entity is a term from Domain-Driven Design that covers a Domain Object that has a long-term identity unrelated to a particular object instance. This may sound abstract and theoretical, but it just means that it represents an object that lives beyond arbitrary bits in memory. While any .NET object instance has an in-memory address (identity), an Entity has an identity that lives across process lifetimes. We often use databases and primary keys to identify Entities and ensure that we can persist and read them even if the host computer reboots.

The Domain Object Product is an Entity because the concept of a product has a much longer lifetime than a single process and we use a Product ID to identify it in the ProductRepository.

A Data Transfer Object (DTO), on the other hand, exists only for the purpose of being transferred from one application tier to another. While an Entity may encapsulate a lot of behavior, a DTO is just a structure of data without behavior.

When exposing a Domain Model to external systems, we often do it with services and DTOs since we can never be sure that the other system can share our type system (it may not even use .NET). In such situations, we always need to map between the Entities and the DTOs.

The bottom line is that we end up with a service implementation with two Dependencies, and since both are mandatory we wish to use Constructor Injection. Here is the service's constructor signature:

So far we have been happily ignoring the elephant in the room: how do we get WCF to correctly wire up an instance of ProductManagementService?

Wiring up ProductManagementService in WCF

As illustrated in figure 1, the Composition Root in WCF is the triplet of ServiceHostFactory, ServiceHost and IInstanceProvider. To wire up a service with Constructor Injection, we must supply custom implementations of all three.

Tip

You can write completely reusable implementations that wrap your favorite DI Container in those three types and use it to implement IInstanceProvider. Many people have already done that, so you can probably find a readymade set for your chosen DI Container.

Note

In this example, I implement a hardwired container using Poor Man’s DI. I have chosen to encapsulate the hardcoded dependencies in a custom container class to give you a good idea about how you could create a reusable solution based on a particular DI Container.

Let us start with the custom ServiceHostFactory, which is our true entry point to a WCF service. Listing 1 shows the implementation.

Listing 1: Custom ServiceHostFactory

The custom CommerceServiceHostFactory derives from ServiceHostFactory with the single purpose of wiring up ProductManagementService instances. It will use a custom CommerceServiceContainer to do the actual work, so it creates an instance of the container in its constructor. You can easily expand this example to use a true DI Container by simply creating and configuring an instance of that container instead.

When asked to create a ServiceHost, it returns a new CommerceServiceHost with the configured container if the requested service type is appropriate.

The CommerceServiceHost is responsible for assigning appropriate behaviors to all the service types it hosts. In our case, we only want to add a single behavior that assigns the desired IInstanceProvider to the services. All this work we can accomplish in the constructor shown in listing 2, and the base class will take care of the rest for us.

Listing 2: Custom ServiceHost

The CommerceServiceHost class derives from ServiceHost, which is a concrete class that does all the heavy lifting for us.

In most cases, we will only host a single service type (in this particular case, ProductManagementService) but we are allowed to host multiple services, which means that we must add the IInstanceProvider to them all. The ImplementedContracts property is a dictionary so we must loop over its Values to target them all.

For each service type, we initialize a new instance of the custom CommerceInstanceProvider class with the container. Since it doubles as a behavior, we can add it to the service's Behaviors (#4).

The last part of our custom WCF triplet is the CommerceInstanceProvider that doubles as both IInstanceProvider and IContractBehavior. It's a very simple implementation but, since it implements two different interfaces with complex signatures, it can look a bit daunting if you see it in one go. I will rather show the code a little at a time, but figure 3 provides an overview.

Figure 3: CommerceInstanceProvider implements both IInstanceProvider and IContractBehavior, so we are required to implement seven methods, although we can leave three empty and the other four as one-liners.

CommerceInstanceProvider implements both IInstanceProvider and IContractBehavior, so we are required to implement seven methods, although we 

can leave three empty and the other four as one-liners.

Listing 3 shows the class declaration and the constructor. Nothing much goes on here apart from the use of Constructor Injection to inject the container. Normally, we use Constructor Injection to announce to a DI Container that a class requires some Dependencies, but here it is backwards as we inject the container itself. This is normally a big code smell since it usually indicates intent to use the Service Locator anti-pattern, but it is necessary here since we are implementing the Composition Root.

Listing 3: CommerceInstanceProvider class declaration and constructor

The CommerceInstanceProvider implements both IInstanceProvider and IContractBehavior. We supply the container through standard Constructor Injection. In this sample, we use the custom CommerceServiceContainer. However, replacing it with a general-purpose DI Container is a trivial exercise.

The IInstanceProvider implementation in listing 4 is used by the WCF runtime to create instances of the ProductManagementService class.

Listing 4: IInstanceProvider implementation

The WCF runtime invokes one of the GetInstance methods to get an instance of the requested service type, so we simply ask the container to wire up the ProductManagementService with all its required Dependencies.

When the service operation has completed, the WCF runtime is nice to ask us to release the instance, and once again we delegate this work to the container.

The other part of CommerceInstanceProvider is the IContractBehavior implementation. The only reason we implement this interface is to allow us to add it to the list of behaviors as shown in listing 2. All of the methods on the IContractBehavior interface return void, so we can leave most of them empty as we do not need to implement them.

Listing 5 shows the implementation of the only method we care about.

Listing 5: Core implementation of IContractBehavior

We only need to do one exceedingly simple thing in this method. The WCF runtime calls this method and passes an instance of DispatchRuntime, which allows us to tell it that it should be using this particular IInstanceProvider implementation. Recall that CommerceInstanceProvider also implements IInstanceProvider. The WCF runtime now knows which IInstanceProvider to use and it can subsequently invoke the GetInstance method shown in listing 4.

This seems like a lot of code to implement to enable DI for WCF and I have even yet to show you the implementation of CommerceServiceContainer.

Tip

Remember that you can easily write reusable versions of these three classes that wrap your favorite DI Container and package that implementation into a library. Many developers have already done that, so you can likely find a suitable ready-made library on the internet.

The container is the last piece of the WCF DI puzzle.

Implementing the specialized container

The CommerceServiceContainer is a specialized container with a single purpose of wiring up the ProductManagementService class. Recall that this class requires instances of ProductRepository and IContractMapper as Dependencies.

With the entire WCF infrastructure out of the way, the container is free to concentrate on wiring up the dependency graph.

Note

Besides adhering nicely to the Single Responsibility Principle, this separation of concerns should give you a good idea that you can replace this specialized container with a general-purpose DI Container because there's no WCF-specific code present.

The ResolveProductManagementService method wires up the instance with Poor Man’s DI as shown in listing 6.

Listing 6: Resolving ProductManagementService

In a sense, when it comes to resolving a dependency graph, it often pays to work our way backwards. We know we need to return an instance of ProductManagementService with ProductRepository and IContractMapper instances. The IContractMapper instance is easy to create, but the ProductRepository requires a bit more work.

We wish to use SqlProductRepository, but to do that we need a connection string that we can read from the web.config file.

If you wish to host the service in your own application, you can now do that by creating a new instance of the CommerceServiceHostFactory class and invoke its CreateServiceHost method with the correct parameters. It will return a CommerceServiceHost instance that you can open and it will figure out the rest for you and host the ProductManagementService.

If, however, you wish to host the service on IIS, there's one more step that you must take.

Hosting ProductManagementService on IIS

On IIS, we do not manually create new instances of CommerceServiceHostFactory. Instead, we must tell IIS to do it on our behalf. This can be done in an .svc file by supplying the Factory attribute:

This .svc file instructs IIS to use CommerceServiceHostFactory every time it needs to create an instance of the ProductManagementService class. It is a requirement that the ServiceHostFactory in question has a default constructor, but this is also the case in this example.

Enabling DI in WCF is harder than it should be, but at least it is possible and the end result is entirely satisfactory. We can use whichever DI Container we would like and we end up having a proper Composition Root.

Summary

Object Composition is one of three important dimensions of DI (the others being Lifetime Management and Interception). Some frameworks make it easy for us. When writing console applications and Windows clients (WPF or Windows Forms), we are more or less in direct control of what is happening at the application's entry point. This provides us with a distinct and easily implemented Composition Root right at the entry point.

Other frameworks like ASP.NET MVC or WCF make us work a little harder, but they still provide the Seams we can use to define how the application should be composed. In WCF, the Seam almost appears as if it is accidentally there. Although it is somewhat more roundabout than implementing a single interface, we can still achieve all the DI goodness we could wish for.

Get 40% discount

DotNetSlacker readers can get 40% off the full print book or ebook at www.manning.com using the promo code dns40 at checkout.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Manning Publications

Manning Publication publishes computer books for professionals--programmers, system administrators, designers, architects, managers and others. Our focus is on computing titles at professional levels. We care about the quality of our books. We work with our authors to coax out of them the best writi...

This author has published 33 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


WCF 4.0 Features: Part 1
In this article we will see in detail three exciting features of WCF 4.0
WCF as a Web Role and Worker Role in Windows Azure - Part 1
In this article we are going to see how WCF plays a major role in application development on Windows...
Walkthrough on WCF 4.0 Service in Silverlight 4.0
In this article we will see, how to create a WCF 4.0 Service.
Create your first RESTful service with WCF
Vishal Nayan explains how to create a RESTful service with WCF.
Calling WCF Duplex service In Silverlight
Vishal Nayan shows how to create a duplex WCF service at server side and let Silverlight access it a...

You might also be interested in the following related blog posts


A Unique Silverlight Viewer for Reporting Services Aimed to Display Reports from Reporting Services in Silverlight Applications. read more
WCF 4: Higher Default Throttling Settings for WCF Services read more
Using FindBySubjectDistinguishedName read more
Intersoft Solutions Releases WebUI Studio 2009 The Worlds Most Innovative Web Development Toolkit read more
WCF defaults stifles loose coupling read more
CodeDigest.Com Article,Codes,FAQs - April,2009 read more
Visual Studio .NET Architecture Templates read more
Securing Applications Built on Silverlight and WCF read more
More reminders / gotchas from the trenches with Azure read more
Enabling Click to Communicate in Web Applications read more
Top
 
 
 

Please login to rate or to leave a comment.