We're trying to use Dependency Injection for a WCF Service. The Service has a dependency on a Unity Container. The container is used to find the appropriate class that implements an IJob
Interface (based on a JobKey
parameter in the method call) and calls a method on it.
The Service is being hosted in MVC2. I've omitted as much irrelevant stuff as possible from the snippets below. Full code available if required...
What I've done so far:
Based on this MSDN Article, I've created a custom InstanceProvider
which should instantiate my service and pass it a container.
I then created a very noddy ServiceBehavior
to use the InstanceProvider
and finally a BehaviorExtension
which just returns the ServiceBehavior
.
Public Class WCFDIInstanceProvider
Implements IInstanceProvider
Private ServiceType As Type
Private Property _Container As IUnityContainer
Private ReadOnly Property Container As IUnityContainer
Get
If _Container Is Nothing Then
_Container = InitialiseContainer()
End If
Return _Container
End Get
End Property
Public Sub New(ByVal ServiceType As Type)
Me.ServiceType = ServiceType
End Sub
Private Function InitialiseContainer() As IUnityContainer
'Code which scans assemblies and populates the container as appropriate
'I'm confident this code works as I've tested it elsewhere
Return Container
End Function
Public Function GetInstance(ByVal instanceContext As System.ServiceModel.InstanceContext) As Object Implements System.ServiceModel.Dispatcher.IInstanceProvider.GetInstance
Return GetInstance(instanceContext, Nothing)
End Function
Public Function GetInstance(ByVal instanceContext As System.ServiceModel.InstanceContext, ByVal message As System.ServiceModel.Channels.Message) As Object Implements System.ServiceModel.Dispatcher.IInstanceProvider.GetInstance
Return Container.Resolve(Me.ServiceType)
End Function
End Class
And the ServiceBehavior
:
Public Class WCFDIServiceBehavior
Implements IServiceBehavior
Public Sub ApplyDispatchBehavior(ByVal serviceDescription As System.ServiceModel.Description.ServiceDescription, ByVal serviceHostBase As System.ServiceModel.ServiceHostBase) Implements System.ServiceModel.Description.IServiceBehavior.ApplyDispatchBehavior
For Each ChannelDispatcherBase As ChannelDispatcherBase In serviceHostBase.ChannelDispatchers
Dim ChannelDispatcher As ChannelDispatcher = TryCast(ChannelDispatcherBase, ChannelDispatcher)
If ChannelDispatcher IsNot Nothing Then
For Each Dispatcher As EndpointDispatcher In ChannelDispatcher.Endpoints
Dispatcher.DispatchRuntime.InstanceProvider = New WCFDIInstanceProvider(serviceDescription.ServiceType)
Next
End If
Next
End Sub
And finally, the really noddy BehaviorExtension:
Public Class WCFDIBehaviorExtension
Inherits BehaviorExtensionElement
Public Overrides ReadOnly Property BehaviorType As System.Type
Get
Return GetType(WCFDIServiceBehavior)
End Get
End Property
Protected Overrides Function CreateBehavior() As Object
Return New WCFDIServiceBehavior
End Function
End Class
The WCF Config tool seems to like all of the above and has generated the following Config XML:
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"
aspNetCompatibilityEnabled="true">
<serviceActivations>
<add relativeAddress="WebJob.svc"
service="MyApplication.WebJobService"
factory="System.ServiceModel.Activation.ServiceHostFactory" />
</serviceActivations>
</serviceHostingEnvironment>
<standardEndpoints>
<mexEndpoint>
<standardEndpoint name="WebJobServiceMex" />
</mexEndpoint>
</standardEndpoints>
<behaviors>
<serviceBehaviors>
<behav开发者_StackOverflow社区ior name="WCFDIServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<WCFDIBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="WebJobService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="httpBinding"
name="HTTPEndpoint" contract="MyApplication.JobService.Common.IWebJobService" />
<endpoint binding="mexTcpBinding" bindingConfiguration="" name="mexEndpoint" />
</service>
</services>
The exception I get is:
System.ServiceModel.ServiceActivationException: The service '/MyAppDir/WebJob.svc' cannot be activated due to an exception during compilation. The exception message is: 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.
System.InvalidOperationException: 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.
WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/13982700
Exception: System.ServiceModel.ServiceActivationException: The service '/MyAppDir/WebJob.svc' cannot be activated due to an exception during compilation. The exception message is: 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.. ---> System.InvalidOperationException: 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.
at System.ServiceModel.Dispatcher.InstanceBehavior..ctor(DispatchRuntime dispatch, ImmutableDispatchRuntime immutableRuntime)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime..ctor(DispatchRuntime dispatch)
at System.ServiceModel.Dispatcher.DispatchRuntime.GetRuntimeCore()
at System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpened()
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
[Blah]
Process Name: w3wp
Process ID: 2108
Which makes sense assuming it's not applying my custom ServiceBehavior
- The default ServiceBehavior's InstaceProvider can't instantiate the service.
Some things to note: If I add a parameterless constructor to my service, I don't get an exception (but of course, I don't get passed a container either) so I'm fairly confident I've found the source of the problem
Can someone please point out what I'm doing wrong?
I guess that problem is in <service name="WebJobService">.
Service element does not contain behaviorConfiguration. Also name attribute must normally contain type name with namespaces. So in your case it should be MyApplication.WebJobService
.
You can also check these articles for alternative implementations 1, 2.
I'd simplify the problem. It's not about Unity, it's about instantiating your service and passing a parameter to the constructor. If that's an issue, consider using property injection.
I'd normally have the service instantiate the Unity container, which avoids the issue entirely.
"Code which scans assemblies and populates the container" makes it sound like MEF might be a better fit for you than Unity.
EDIT: I think I'm wrong to recommend property injection. You don't want Unity to create the instance of the service at all. Since UnityContainer.Resolve is thread safe (although configuration requires locking), you could have the service create and own a static instance of the container. The container instance will resolve other dependencies.
精彩评论