具有无参数构造函数的 WCF 自定义 ServiceBehavior/InstanceProvider

发布于 2024-10-11 13:52:01 字数 6455 浏览 3 评论 0原文

我们正在尝试对 WCF 服务使用依赖注入。该服务依赖于 Unity 容器。该容器用于查找实现 IJob 接口(基于方法调用中的 JobKey 参数)的适当类并调用其上的方法。

该服务托管在 MVC2 中。我从下面的片段中省略了尽可能多的不相关的内容。如果需要,可以提供完整的代码...

到目前为止我所做的:

基于此 MSDN 文章,我创建了一个自定义InstanceProvider,它应该实例化我的服务并向其传递一个容器。

然后,我创建了一个非常简单的 ServiceBehavior 来使用 InstanceProvider,最后创建了一个仅返回 ServiceBehaviorBehaviorExtension

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

还有 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

最后,真正的行为扩展:

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

WCF 配置工具似乎喜欢上述所有内容,并生成了以下配置 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>
    <behavior 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>

我得到的例外是:

System.ServiceModel.ServiceActivationException:服务 由于执行期间出现异常,无法激活“/MyAppDir/WebJob.svc” 汇编。异常消息为: 提供的服务类型 无法作为服务加载,因为它没有默认值 (无参数)构造函数。要解决该问题,请添加默认值 构造函数到该类型,或将该类型的实例传递给主机。

System.InvalidOperationException:提供的服务类型无法 被加载为服务,因为它没有默认值 (无参数)构造函数。要解决此问题,请添加默认值 构造函数到该类型,或将该类型的实例传递给主机。

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

假设它没有应用我的自定义 ServiceBehavior ,这是有道理的 - 默认 ServiceBehavior 的 InstaceProvider 无法实例化该服务。

需要注意的一些事情:如果我向我的服务添加无参数构造函数,我不会收到异常(但当然,我也不会传递容器),所以我相当有信心我已经找到了来源问题

有人可以指出我做错了什么吗?

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>
    <behavior 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

清风疏影 2024-10-18 13:52:01

我猜问题出在 . Service 元素不包含behaviorConfiguration。此外,名称属性通常必须包含带有命名空间的类型名称。因此,在您的情况下,它应该是 MyApplication.WebJobService

您还可以查看这些文章以获取替代实现 12

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.

墨落成白 2024-10-18 13:52:01

我会把问题简单化。这与 Unity 无关,而是与实例化服务并将参数传递给构造函数有关。如果这是一个问题,请考虑使用属性注入

我通常会让服务实例化 Unity 容器,这完全避免了这个问题。

“扫描程序集并填充容器的代码”听起来 MEF 可能比 Unity 更适合您。

编辑:我认为我建议属性注入是错误的。您根本不希望 Unity 创建服务实例。由于 UnityContainer.Resolve 是线程安全的(尽管配置需要锁定),您可以让服务创建并拥有容器的静态实例。容器实例将解决其他依赖关系。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文