WCF Custom ServiceBehavior / InstanceProvider с конструктором без параметров - PullRequest
1 голос
/ 13 января 2011

Мы пытаемся использовать Dependency Injection для службы WCF.Служба зависит от контейнера Unity.Контейнер используется для поиска соответствующего класса, который реализует интерфейс IJob (основанный на параметре JobKey в вызове метода) и вызывает для него метод.

Сервис размещается в MVC2.Я опустил как можно больше ненужных вещей из приведенных ниже фрагментов.Полный код доступен, если требуется ...

Что я уже сделал:

Основываясь на этой статье MSDN , я создал пользовательский InstanceProvider, которыйЯ должен создать экземпляр моего сервиса и передать ему контейнер.

Затем я создал очень noddy ServiceBehavior для использования InstanceProvider и, наконец, BehaviorExtension, который просто возвращает 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

И 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

И, наконец, действительно 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

Инструмент WCF Config, кажется, нравится всемиз вышеупомянутого и сгенерировал следующий 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>

Исключение, которое я получаю:

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 -InstaceProvider из ServiceBehavior по умолчанию не может создать экземпляр службы.

Некоторые вещи, на которые следует обратить внимание: если я добавляю конструктор без параметров в свою службу, я не получаю исключения (но, конечно, я не получаюили контейнер), так что я вполне уверен, что нашел источник проблемы

Может кто-нибудь указать, что я делаю неправильно?

Ответы [ 2 ]

1 голос
/ 13 января 2011

Я бы упростил задачу.Речь идет не о Unity, а об инстанцировании вашего сервиса и передаче параметра конструктору.Если это проблема, рассмотрите возможность использования инъекции свойства .

. Обычно у меня есть служба создания экземпляра контейнера Unity, что полностью исключает проблему.

"Код, который сканируетсобирает и заполняет контейнер "это звучит так, как будто MEF может быть лучше для вас, чем Unity.

РЕДАКТИРОВАТЬ: Я думаю, что я не рекомендую свойствоинъекции.Вы не хотите, чтобы Unity вообще создала экземпляр службы.Поскольку UnityContainer.Resolve является поточно-ориентированным (хотя для конфигурации требуется блокировка), вы можете создать службу и иметь статический экземпляр контейнера.Экземпляр контейнера разрешит другие зависимости.

1 голос
/ 13 января 2011

Я предполагаю, что проблема в <service name="WebJobService">. Элемент службы не содержит поведениеConfiguration.Также атрибут name обычно должен содержать имя типа с пространствами имен.Так что в вашем случае это должно быть MyApplication.WebJobService.

. Вы также можете проверить эти статьи для альтернативных реализаций 1 , 2 .

...