14

私の WCF サービスには、受信メッセージを XML スキーマに対して生の XML として検証するためのカスタム メッセージ インスペクターがあります。メッセージ インスペクターには、いくつかの依存関係があります (ロガーや XML スキーマ コレクションなど)。私の質問は、これらのカスタム動作をインスタンス化し、依存関係を自動的に注入するために、依存性注入フレームワーク (私は現在 Ninject を使用しています) を使用できますか?

概念を示す簡単な例を作成しました。

using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Ninject.Extensions.Logging;

public class LogMessageInspector : IDispatchMessageInspector
{
    private readonly ILogger log;

    public LogMessageInspector(ILogger log)
    {
        this.log = log;
    }

    public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
    {
        LogMessage(ref request);
        return null;
    }

    public void BeforeSendReply(ref Message reply, object correlationState)
    {
        LogMessage(ref reply);
    }

    private void LogMessage(ref Message message)
    {
        //... copy the message and log using this.log ...
    }
}

public class LogMessageBehavior : IEndpointBehavior
{
    private readonly IDispatchMessageInspector inspector;

    public LogMessageBehavior(IDispatchMessageInspector inspector)
    {
        this.inspector = inspector;
    }

    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this.inspector);
    }

    public void Validate(ServiceEndpoint endpoint) { }
}

anと aを に挿入ILoggerするにはどうすればよいですか?LogMessageInspectorLogMessageInspectorLogMessageBehavior

2番目の質問、これはやり過ぎですか?

編集:Ninjectを使用して動作を作成するため、コードでサービスを構築すると、これを機能させることができます。ただし、構成を介してサービスを構成する場合は、拡張するクラスを追加する必要がありますBehaviorExtensionElement。このクラスは WCF によって作成され、代わりに Ninject によって作成されるようにする方法が見つからないようです。コードで設定:

static void Main(string[] args)
{
    using (IKernel kernel = new StandardKernel())
    {
        kernel.Bind<IEchoService>().To<EchoService>();
        kernel.Bind<LogMessageInspector>().ToSelf();
        kernel.Bind<LogMessageBehavior>().ToSelf();

        NinjectServiceHost<EchoService> host = kernel.Get<NinjectServiceHost<EchoService>>();
        ServiceEndpoint endpoint = host.AddServiceEndpoint(
            typeof(IEchoService),
            new NetNamedPipeBinding(),
            "net.pipe://localhost/EchoService"
        );
        endpoint.Behaviors.Add(kernel.Get<LogMessageBehavior>());

        host.Open();

        Console.WriteLine("Server started, press enter to exit");
        Console.ReadLine();
    }
}

これは正常に動作しますが、 my を介して構成されたときに動作を作成する方法がわかりませんapp.config:

<system.serviceModel>
    <services>
        <service name="Service.EchoService">
            <endpoint address="net.pipe://localhost/EchoService" 
                      binding="netNamedPipeBinding"
                      contract="Contracts.IEchoService" 
                      behaviorConfiguration="LogBehaviour"
            />
        </service>
    </services>
    <extensions>
        <behaviorExtensions>
            <add name="logMessages" type="Service.LogMessagesExtensionElement, Service" />
        </behaviorExtensions>
    </extensions>
    <behaviors>
        <endpointBehaviors>
            <behavior name="LogBehaviour">
                <logMessages />
            </behavior>
        </endpointBehaviors>
    </behaviors>
</system.serviceModel>
public class LogMessagesExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(LogMessageBehavior); }
    }

    protected override object CreateBehavior()
    {
        //how do I create an instance using the IoC container here?
    }
}
4

3 に答える 3

4

ILogger を LogMessageInspector に、LogMessageInspector を LogMessageBehavior に挿入するにはどうすればよいですか?

アプローチはここで説明されています

アップデート

私が間違っている場合は修正してください。しかし、問題は、Ninject カーネルのインスタンスをどのように取得できるかということになると思いますBehaviorExtensionElement.CreateBehavior。答えは、ホスティング シナリオによって異なります。IIS でホストされている場合は、次のようなものを に追加できますNinjectWebCommon

public static StandardKernel Kernel
{
    get { return (StandardKernel)bootstrapper.Kernel; }
}

あなたは自己ホスティングしているように見えるので、カーネルの静的インスタンスにも行きたいと思うかもしれません。しかし、私の見解では、これはあまり良い考えではありません。

BehaviorExtensionElement構成ファイルを介して動作を構成できる必要があるため、必要でない限り、実際には独自のアプローチに投票し、プログラムで動作を構成します。

これはやり過ぎですか?

それは依存しますが、実装を単体テストする場合は絶対にそうではありません。

于 2012-11-24T13:34:51.057 に答える
1

XML スキーマに対して未加工の XML を検証する代わりに、よりオブジェクト指向のアプローチを採用してみませんか? たとえば、各操作を単一のメッセージ ( DTO ) としてモデル化し、実際のロジックを汎用インターフェイスの背後に隠すことができます。MoveCustomer(int customerId, Address address)したがって、メソッドを含む WCF サービスを使用する代わりに、クラスがあり、実際のロジックは、単一のメソッドでインターフェイスをMoveCustomerCommand { CustomerId, Address }実装するクラスによって実装されます。ICommandHandler<MoveCustomerCommand>Handle(TCommand)

この設計には、次の利点があります。

  • システム内のすべての操作は、独自のクラス ( SRP )を取得します。
  • これらのメッセージ/コマンド オブジェクトは WCF コントラクトを取得します
  • WCF サービスには、1 つのメソッドを持つ 1 つのサービス クラスのみが含まれます。これにより、保守性の高い WCF サービスが実現します。
  • インターフェース ( OCP )のデコレーターを実装することにより、分野横断的な懸念事項を追加できます。ICommandHandler<T>
  • (たとえば属性を使用して) メッセージ/コマンド オブジェクトに検証を配置できるようにし、デコレータを使用してこの検証を再度追加できるようにします。

単一の汎用インターフェイスに基づく設計を適用するICommandHandler<TCommand>と、すべての実装に適用できる汎用デコレータを非常に簡単に作成できます。一部のデコレーターは、WCF サービス内で実行する場合にのみ適用する必要がある場合があります。他のデコレーター (検証など) は、他のアプリケーションの種類にも必要になる場合があります。

メッセージは次のように定義できます。

public class MoveCustomerCommand
{
    [Range(1, Int32.MaxValue)]
    public int CustomerId { get; set; }

    [Required]
    [ObjectValidator]
    public Address NewAddress { get; set; }
}

このメッセージは、顧客をCustomerId提供されたに移動する操作を定義しますNewAddress。属性は、有効な状態を定義します。これにより、.NET DataAnnotations または Enterprise Library Validation Application Block を使用して、オブジェクト ベースの検証を簡単に実行できます。これは、保守が非常に困難な XSD ベースの XML 検証を作成するよりもはるかに優れています。これは、現在解決しようとしている複雑な WCF 構成を行うよりもはるかに優れています。この検証を WCF サービス内で行う代わりに、次のように、すべてのコマンドが検証されるようにするデコレータ クラスを定義するだけです。

public class ValidationCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> decoratedHandler;

    public ValidationCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler)
    {
        this.decoratedHandler = decoratedHandler;
    }

    public void Handle(TCommand command)
    {
        // Throws a ValidationException if invalid.
        Validator.Validate(command);

        this.decoratedHandler.Handle(command);
    }
}

このValidationCommandHandlerDecorator<T>デコレーターは、あらゆるタイプのアプリケーションで使用できます。WCFだけではありません。デフォルトでは、WCF は throw された を処理しValidationExceptionないため、WCF 用に特別なデコレータを定義できます。

public class WcfFaultsCommandHandlerDecorator<TCommand>
    : ICommandHandler<TCommand>
{
    private ICommandHandler<TCommand> decoratedHandler;

    public WcfFaultsCommandHandlerDecorator(
        ICommandHandler<TCommand> decoratedHandler)
    {
        this.decoratedHandler = decoratedHandler;
    }

    public void Handle(TCommand command)
    {
        try
        {
            this.decoratedHandler.Handle(command);
        }
        catch (ValidationException ex)
        {
            // Allows WCF to communicate the validation 
            // exception back to the client.
            throw new FaultException<ValidationResults>(
                ex.ValidationResults);
        }
    }
}

DI コンテナーを使用しない場合、次のように新しいコマンド ハンドラーを作成できます。

ICommandHandler<MoveCustomerCommand> handler = 
    new WcfFaultsCommandHandlerDecorator<MoveCustomerCommand>(
        new ValidationCommandHandlerDecorator<MoveCustomerCommand>(
            // the real thing
            new MoveCustomerCommandHandler()
        )
    );

handler.Handle(command);

このタイプの設計について詳しく知りたい場合は、次の記事をお読みください。

于 2012-11-25T18:17:12.710 に答える
-1

LogMessageBehavior で BehaviorExtensionElement もその基本クラスとして使用するようにすると、次のことができるようになります。

public override Type BehaviorType
{
    get { return this.GetType(); }
}

protected override object CreateBehavior()
{
    return this;
}
于 2012-11-25T00:04:43.887 に答える