Let's take then my Service Locator sample
public class CustomerService:ICustomerServiceand rework it to use Dependency Injection
{
#region ICustomerService Members
public Customer GetCustomer(long id)
{
ICustomerDataAccess da;
da = Global.Container.Resolve<ICustomerDataAccess>();
return da.GetCustomer(id);
}
#endregion
}
public class CustomerService:ICustomerServiceYou can notice that with those changes our service class is no longer dependant on the DI container for resolving the CustomerDataAccess implementation like it is in the Service Locator example. Instead, the Data access dependency is a property that can be injected by an external agent, which can be whatever DI container we want to use or even a simple factory... Let's focus on this sample though!
{
#region properties
private ICustomerDataAccess customerDataAccess;
public ICustomerDataAccess CustomerDataAccess
{
get { return customerDataAccess; }
set { customerDataAccess = value; }
}
#endregion
#region ICustomerService Members
public Customer GetCustomer(long id)
{
return CustomerDataAccess.GetCustomer(id);
}
#endregion
}
The next step is to create a Castle configuration file called "Objects.xml" where both service and data access objects are defined.
<?xml version="1.0" encoding="utf-8" ?>As you can see there is no need to connect the DataAccess dependency to the Service object in the configuration file; Castle automatically wires it up.
<configuration>
<components>
<component id="CustomerService"
service="DISample.Service.ICustomerService, DISample.Service"
type="DISample.Service.CustomerService, DISample.Service">
</component>
<component id="CustomerDataAccess"
service="DISample.DataAccess.ICustomerDataAccess, DISample.DataAccess"
type="DISample.DataAccess.CustomerDataAccess, DISample.DataAccess">
</component>
</components>
</configuration>
Now that the Service code is ready for injection and the objects are defined in a configuration file, it's time to use the WCF facility to do the actual service injection.
In order to install the dependency injection behavior, Castle facility uses a a custom WCF ServiceHost. Then, for a non-IIS 6.0 environment and asuming that the service and the endpoint configuration are located in an external configuration file, our WCF host creation would look something like this.
WindsorContainer container = new WindsorContainer("Objects.xml");We instantiate the Windsor container with our object configuration file so as to be used by the custom service host... and that's all for non-IIS6.0 hosts!
Uri uri = new Uri("net.tcp://localhost/DISample");
WindsorServiceHost host = new WindsorServiceHost(container.Kernel, typeof(CustomerService), uri);
host.Open();
As you may already know, ServiceHost instatiatation model for WCF services host under II6.0 is different: We don't explictly create the ServiceHost; instead ServiceHosts are created by a ServiceHostFactory on each HTTP request.
WCF castle facility provides a custom ServiceHostFactory that can be specified for its use in IIS6 .svc service files. Let's change the default CustomerService.svc accordingly.
<%@ ServiceHost Service="CustomerService"
Factory="Castle.Facilities.WcfIntegration.WindsorServiceHostFactory, Castle.Facilities.WcfIntegration" %>Where the value of attribute Service must match either the id of our service in Castle configuration file or the service type itself.Are we missing anything? We haven't created the Windsor Container yet, have we?. Like I said before in II6.0 environments ServiceHosts are created by http request, but would it be wise to apply the same approach for creating the WindsorContainer? I don't think so! Don't panic though, WCF facility has already covered that. Since we are in a web environment, we can use the Global.asax Application_Start event to create our Windsor Container and then register it through an available method in the custom ServiceHostFactory.
public class Global : System.Web.HttpApplicationWe are done! What's next then? One of the things that I pointed out in the first post of this series is that no AOP features can be applied upon objects that are not handled by the DI container... That's no longer a problem for us!
{
protected void Application_Start(object sender, EventArgs e)
{
WindsorContainer container = new WindsorContainer("Objects.xml");
WindsorServiceHostFactory.RegisterContainer(container.Kernel);
}
}
In the next post of these series I'll show how we can use castle interceptors to apply cross cutting concerns upon service objects.
Hey man - this is really neat stuff. I haven't seen much going on in terms of loosely coupling for WCF and with Castle , this is surely a very good sample to promote re use and decoupling of cross cutting implications.
ReplyDeletewill be looking forward to your next blog post covering this issue with castle. thanks for this.
Good one. Quite interesting to see that you are using Castle. That would be my choice as well (if I have one, arghhh).
ReplyDeleteThanks Stan!
ReplyDeleteI have just started using Castle, but as a former Spring.NET user, it seems to me that Castle is a lighter framework and somewhat easier to use probably because it mainly tries to target Dependency Injection concerns... It's just my newbie personal opinion though!
Btw, I found very interesting the ammendments that you applied to Oran's solution, it looks to me that your behaviour extension solution is more configurable than the Custom Host approach and besides it's nice to be able to define different service instance objects of the same type and then configure the container to pick up a particular one... I might steal your code and adapt it for Castle!
I can't find any castle dlls from the windsor install containing WindsorServiceHost. I also got latest from the SVN trunk and can't find it either. Where can I find the WindsorServiceHost?
ReplyDeletethanks!
Hi Scott,
ReplyDeleteThe WCF facility has changed quite a bit since I wrote this post, there are plenty of new stuff now for both proxy and sever side. I recommend you to take a look into the sample project coming along with the code from the trunk
Hope this helps,
Javi
I'm looking to use WAS for my hosting. How do I instantiate the Windsor Container and register it. I see that the example that you provide does it in the Global.asax but this does not get called in WAS.
ReplyDelete