Sunday, March 9, 2008

Ammendments to the Orand's WCF Spring.NET Dependency Injection solution.

This is just a log entry. Code and samples to be published later (http://plainoldstan.blogspot.com/2008/03/source-code-for-amendments-to-oran.html). I'll also review the Spring.NET current implementation to see if there is something to offer to Mark and Bruno.

Although Spring.NET seems?? to have already implemented the WCF DI Instance Provider, it is good to have one in your very own wcf helper library.
[Note 23-Apr-2008, it turns out that there is no integration with wcf in Spring.NET 1.1 production release]

Starting point and credits should go to Oran: http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html

That and this article/implementation leave the question of instances management and recycling wide open and subject to revisit a bit later.

One of the limitations of the above implementation is the following check:

public object GetInstance(InstanceContext instanceContext, Message message) 
{
object result = null;
IApplicationContext context = ContextRegistry.GetContext();
string[] objectNames = context.GetObjectNamesForType(_serviceType);
if (objectNames.Length != 1) {
throw new InvalidOperationException(
string.Format(CultureInfo.InvariantCulture,
"There must exist exactly one <object> definition for the {0} service in the Spring configuration", _serviceType.Name));
}




The above code uses GetObjectNamesForType method to find the object definition to instantiate. Though this effectively contradicts with scenarios when you need to have multiple objects defined with the same type by different names in the spring configuration.



For example the bellow spring.net configuration snippet configures two objects of the same type in the different way. If one of them is to be exposed as a wcf service, the original solution will fail during the above mentioned check.



      <object name="DataTableService" type="Dsi.Tools.Common.DataTables.DataTableService, Dsi.Tools.Common" singleton="false">
<constructor-arg name="dataProvider" ref="dataProvider"/>
</object>
<object name="dataProvider" type="Dsi.Tools.Common.DataTables.ReportDataProvider, Dsi.Tools.Common">
<constructor-arg name="tableTransformer" ref="reportTransposeTransformer"/>
</object>
<object name="PlainDataTableService" type="Dsi.Tools.Common.DataTables.DataTableService, Dsi.Tools.Common" singleton="false">
<constructor-arg name="dataProvider" ref="plainDataProvider"/>
</object>
<object name="plainDataProvider" type="Dsi.Tools.Common.DataTables.ReportDataProvider, Dsi.Tools.Common">
<!-- No transformer, just brings data as they were provided by the sql sp -->
</object>


Solution:


As I'm used to create BehaviorElements instead of writing custom ServiceHosts to apply behaviours, first thing I did after "stealing" the Oran's code, I implemented a DependencyInjectionBehaviorElement, first without any properties. To resolve the object name vs. object type issue, I only needed to add an extra property to it then:



        [ConfigurationProperty("mappings",
IsDefaultCollection = true, IsRequired = true)]
public ServiceTypeMappingConfigElementCollection Mappings
{

get
{
return base["mappings"] as ServiceTypeMappingConfigElementCollection;
}
}


After writing the corresponding configuration classes code the app/web.config behaviour related configuration looks like:


        <behavior name="DependencyInjectionServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<exceptionHandling exceptionHandlingPolicyName="ServiceLayerBoundary"/>
<dependencyInjection>
<mappings>
<add type="Dsi.Tools.Common.DataTables.DataTableService, Dsi.Tools.Common"
objectName="DataTableService" />
</mappings>
</dependencyInjection>

</behavior>



    <extensions>
<behaviorExtensions>
<add name="dependencyInjection" type="Dsi.Tools.Common.Wcf.DependencyInjectionElement, Dsi.Tools.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<add name="exceptionHandling" type="Dsi.Tools.Common.Wcf.ExceptionHandlingElement, Dsi.Tools.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>



Part of the solution is as well moving the validation code to the Validate method of the Behaviour.



This will result in the exceptions during the service activation, which would be the expected behaviour for the wrongly configured service:



DIBehaviorValidation



another point of validation added is to check if actually the object definition for the mapping exists, if not raise exception during the Validate. Same for the type load error for the service type.



image

No comments: