|
We received feedback regarding our initial Dependency Injection and Unity IoC container implementation.
Feedback 1:
First, in our V1.0 we were abstracting our IoC Container (IContainer) which we were doing in a similar way than Microsoft P&P with ‘Common Service Locator’:
http://commonservicelocator.codeplex.com/
But we acknowledge that it is not really necessary for a standalone application. For instance, P&P CommonServiceLocator was especially made for frameworks/libraries integration more than for a standalone application. Also, if you stick Unity to a common
interface, you won’t be able to use Unity for advanced scenarios like Calls-Interception it you want to make kind of AOP, etc. Therefore, for V2.0 we decided to eliminate that abstraction, using Unity directly.
Feedback 2:
Unity integration with WCF can be improved.
When talking about DI (Dependency Injection) and as we state in our guidance, it is important that the dependencies objects graph must be started from the initial entry point to your Application Server (either WCF or ASP.NET MVC). You should not use container.Resolve()
in any layer. Ok, we were doing that, as the only layer when we had container.Resolve() was at the WCF Service Layer. Then, from the WCF methods, the DI objects graph was created down to all dependencies like Application Services, Domain Services, Repositories,
Unit of Work, etc.
So, we were doing DI (not ServiceLocator pattern), in this way, starting on each WCF Service method:
WCF Service methods (Initially):
public List<CustomerListDTO> FindCustomersInPage(int pageIndex, int pageCount)
{
//Starting point for this DI Object graph
ICustomerAppService customerService = Container.Current.Resolve<ICustomerAppService>();
return customerService.FindCustomers(pageIndex, pageCount);
}
…
This way, we are putting “container.resolve()” sentences only in one layer, the initial layer (WCF Service) and we chose to leave it like that because it is quite easy to understand for anybody even with few WCF and IoC/DI/Unity knowledge. But
still, we would have at least one call to the Unity container per WCF-Service method, like I mentioned.
On the other hand, we acknowledge that this approach is improvable because it would be much better if we could have a deeper integration with WCF. I mean, instead of starting on the WCF methods (kind of a second WCF level), the Dependency Injection should
start with the WCF-Service class and constructor themselves. Doing so, we could have a single line of code calling the Unity container for the whole solution.
Dependency Injection should start from the WCF Service class Inception. :-)
This better approach is going to be initially more complex to understand (just a little), but it is worth to do it and ultimately, our application code and WCF Services will be even thiner (as single line of code for each WCF Service method).
The approach we have chosen is based on a WCF Extensibility point that WCF has which is the
IInstanceProvider interface. Creating a custom InstanceProvider that implements that interface is probably the best way to integrate our IoC container instances creation with WCF Services.
This is our UnityInstanceProvider class which will be invoked automatically by WCF:
public class UnityInstanceProvider : IInstanceProvider
{
Type _serviceType;
IUnityContainer _container;
public UnityInstanceProvider(Type serviceType)
{
if (serviceType == null)
throw new ArgumentNullException("serviceType");
this._serviceType = serviceType;
_container = Container.Current;
}
public object GetInstance(InstanceContext instanceContext,
System.ServiceModel.Channels.Message message)
{
//This is the only call to UNITY container in the whole solution
return _container.Resolve(_serviceType);
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
if (instance is IDisposable)
((IDisposable)instance).Dispose();
}
}
After having our custom InstanceProvider we need a way to tell WCF that we want him to invoke our InstanceProvider. The way to do that in WCF is using
WCF-Behaviors.
So we have created a WCF-Behavior called ‘UnityInstanceProviderServiceBehavior’. We also created it as an Attribute, so we can easily apply it to our WCF Service:
public class UnityInstanceProviderServiceBehavior : Attribute, IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
foreach (var item in serviceHostBase.ChannelDispatchers)
{
var dispatcher = item as ChannelDispatcher;
if (dispatcher != null) // add new instance provider for each end point dispatcher
{
dispatcher.Endpoints.ToList().ForEach(endpoint =>
{
endpoint.DispatchRuntime.InstanceProvider =
new UnityInstanceProvider(serviceDescription.ServiceType);
});
}
}
}
//Other ommitted methods…
}
Then, we simple decorate our WCF Service classes with our behavior, in a similar way we are applying our exception handling attribute, like the following:
[ApplicationErrorHandlerAttribute()] // manage all unhandled exceptions
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
[UnityInstanceProviderServiceBehavior()] //create instance and inject dependencies using unity container
public class ERPModuleService : IERPModuleService
{
//Dependencies aggregating several Application Layer Services of this MODULE
ICustomerAppService _customerAppService;
ISalesAppService _salesAppService;
//WCF Class Constructor where we specify our DEPENDENCIES
public ERPModuleService(ICustomerAppService customerAppService,
ISalesAppService salesAppService)
{
//… Validations ommitted…
// Keep dependencies’ objects graphs created by the Unity Container
_customerAppService = customerAppService;
_salesAppService = salesAppService;
}
//A single line of code per WCF Service Method
//(Errors handling and instances creation are based on WCF behaviours attributes )
public List<CustomerListDTO> FindCustomersByFilter(string filter)
{
return _customerAppService.FindCustomers(filter);
}
//Other WCF Service methods
//…
}
Now, as you can see, our WCF-Services are the simplest, one line of code!. I guess this cannot be thinner… ;-)
These changes are already updated within the CODEPLEX Source-control. You can do a ‘Get Latest Version’ if you want to check it out running the whole app.
Comments/Feedback about this improvement?
We’d really appreciate your constructive feedback in order to improve any area of this current ALPHA version. J
Thanks,
Cesar.
|