2011-01-13 5 views
1

Wir versuchen Dependency Injection für einen WCF-Dienst zu verwenden. Der Dienst ist von einem Unity-Container abhängig. Der Container wird verwendet, um die geeignete Klasse zu finden, die eine IJob Schnittstelle implementiert (basierend auf einem JobKey Parameter im Methodenaufruf) und eine Methode dafür aufruft.WCF Custom ServiceBehavior/InstanceProvider mit parameterlosem Konstruktor

Der Dienst wird in MVC2 gehostet. Ich habe so viel irrelevantes Zeug wie möglich aus den Schnipsel unten weggelassen. Full-Code zur Verfügung, wenn erforderlich ...

Was ich bisher getan:

Basierend auf dieser MSDN Article, ich habe eine benutzerdefinierte InstanceProvider geschaffen, die meinen Dienst instanziiert sollte und er einen Behälter übergeben.

Ich erstellte dann eine sehr noddy ServiceBehavior, um die InstanceProvider und schließlich eine BehaviorExtension zu verwenden, die nur die ServiceBehavior zurückgibt.

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 

Und die 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 

Und schließlich die wirklich 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 

Der WCF-Config-Tool scheint alle oben zu mögen und hat die erzeugte folgende 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> 

Die Ausnahme, die ich bekommen ist:

System.ServiceModel.ServiceActivationException: Der Dienst '/MyAppDir/WebJob.svc' kann nicht aufgrund einer Ausnahme während Kompilierung aktiviert werden. Die Ausnahmebedingungsnachricht lautet: Der angegebene Servicetyp konnte nicht als Dienst geladen werden, da er keinen Standardkonstruktor (parameterlos) hat. Um das Problem zu beheben, fügen Sie dem Typ einen Standard -Konstruktor hinzu oder übergeben Sie eine Instanz des Typs an den Host.

System.InvalidOperationException: Der angegebene Servicetyp konnte nicht als Dienst geladen werden, da kein Standard (parameterlos) -Konstruktor vorhanden ist. Um das Problem zu beheben, fügen Sie dem Typ einen Standard -Konstruktor hinzu oder übergeben Sie eine Instanz des Typs an den 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 

Welchen Sinn unter der Annahme, macht es nicht meine Gewohnheit ist die Anwendung ServiceBehavior - Der Standard ServiceBehavior den InstaceProvider kann den Dienst nicht instanziiert.

Einige Dinge zu beachten: Wenn ich einen parameterlosen Konstruktor zu meinem Dienst hinzufüge, bekomme ich keine Ausnahme (aber natürlich bekomme ich auch keinen Container), also bin ich ziemlich zuversichtlich, dass ich habe gefunden die Quelle des Problems

Kann jemand bitte darauf hinweisen, was ich falsch mache?

+0

@Steven Danke für die Bearbeitung, aber ich dachte, die Ausnahme Dump war ziemlich klar - macht das Duplizieren der Fehlermeldung in einem Zitat wirklich die Qualität zu verbessern? – Basic

+0

Ja, ich glaube es tut. Ich habe diese Frage gefunden, indem ich die Ausnahmebedingung gegoogelt habe, aber nach dem Durchsuchen der Frage konnte ich diese Nachricht nicht finden. Meine Bearbeitung erlaubt anderen, diese Nachricht leichter zu finden, das war mein Ziel. – Steven

Antwort

1

Ich vermute, dass das Problem in <service name="WebJobService">. ist Service-Element enthält behaviorConfiguration. Das name-Attribut muss normalerweise den Typ name mit Namespaces enthalten. In Ihrem Fall sollte es also MyApplication.WebJobService sein.

Sie können diese Artikel auch nach alternativen Implementierungen überprüfen 1, 2.

+0

Ich hatte den Eindruck, dass Service-Verhalten auf alle Dienste angewendet wird - das könnte mein Fehler sein, ich werde morgen im Büro einchecken. Was die Benennung angeht - ich denke, das ist einfach ein Name, der sich auf das Element von einem anderen Punkt im XML bezieht? – Basic

+0

Nein. Sie müssen in der Konfiguration nicht auf die Serviceeinstellungen verweisen. Der Name bindet den Servicetyp an diese Einstellung. Zumindest bei der Instanziierung muss der Name genau so sein wie der Typname mit Namespaces, ansonsten wird die Konfiguration nicht verwendet. –

1

Ich würde das Problem vereinfachen. Es geht nicht um Unity, es geht darum, Ihren Dienst zu instanziieren und einen Parameter an den Konstruktor zu übergeben. Wenn dies ein Problem ist, sollten Sie die Verwendung von property injection in Erwägung ziehen.

Normalerweise würde der Dienst den Unity-Container instanziieren, wodurch das Problem vollständig vermieden wird.

"Code, der Baugruppen scannt und den Container bestückt", klingt wie MEF könnte besser für Sie als Unity passen.

EDIT: Ich denke, ich bin falsch, Immobilien Injektion zu empfehlen. Sie möchten nicht, dass Unity die Instanz des Service überhaupt erstellt. Seit UnityContainer.Resolve is thread safe (obwohl die Konfiguration eine Sperrung erfordert), könnte der Dienst eine statische Instanz des Containers erstellen und besitzen. Die Container-Instanz löst andere Abhängigkeiten auf.

+0

MEF wäre fast sicher eine bessere Lösung - leider, Das Projekt ist für ein neues Startup und wir haben begrenzte Ressourcen - Umstellung auf MEF würde Entwicklungszeit brauchen, die wir ATM nicht entbehren können. Wir denken schon über eine spätere Version nach - Danke, dass Sie es aufgezeigt haben. – Basic