wcfサービスのコンストラクターに値を渡すにはどうすればよいですか?


103

サービスを実装するクラスのコンストラクターに値を渡したいのですが。

ただし、ServiceHostでは、作成する型の名前のみを渡すことができます。コンストラクタに渡す引数は渡されません。

サービスオブジェクトを作成するファクトリを渡すことができるようにしたいと思います。

これまでに見つけたもの:


6
複雑さはWCFに固有であり、WCFを使用しないか、Windsorを使用している場合はWindsorのWCFファシリティなどのよりユーザーフレンドリーなファサードの後ろに隠す以外に、できることは多くありません
Krzysztof Kozmic

回答:


122

カスタムの組み合わせを実装する必要がありますServiceHostFactoryServiceHostIInstanceProvider

このコンストラクター署名のあるサービスを考えます。

public MyService(IDependency dep)

MyServiceを起動できる例を次に示します。

public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IDependency dep;

    public MyServiceHostFactory()
    {
        this.dep = new MyClass();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType,
        Uri[] baseAddresses)
    {
        return new MyServiceHost(this.dep, serviceType, baseAddresses);
    }
}

public class MyServiceHost : ServiceHost
{
    public MyServiceHost(IDependency dep, Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        foreach (var cd in this.ImplementedContracts.Values)
        {
            cd.Behaviors.Add(new MyInstanceProvider(dep));
        }
    }
}

public class MyInstanceProvider : IInstanceProvider, IContractBehavior
{
    private readonly IDependency dep;

    public MyInstanceProvider(IDependency dep)
    {
        if (dep == null)
        {
            throw new ArgumentNullException("dep");
        }

        this.dep = dep;
    }

    #region IInstanceProvider Members

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return this.GetInstance(instanceContext);
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return new MyService(this.dep);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        var disposable = instance as IDisposable;
        if (disposable != null)
        {
            disposable.Dispose();
        }
    }

    #endregion

    #region IContractBehavior Members

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

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

    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceProvider = this;
    }

    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {
    }

    #endregion
}

MyService.svcファイルにMyServiceHostFactoryを登録するか、セルフホスティングシナリオのコードで直接MyServiceHostを使用します。

このアプローチは簡単に一般化できます。実際、一部のDIコンテナーは既にこれを実行しています(手掛かり:WindsorのWCF機能)。


+1(残念ながら、違反の最も深刻なケースではありませんが、#regionsです。明示的なインターフェイスに変換します。自分自身を
インプリメントし

5
セルフホスティングにどのように使用できますか?CreateServiceHostを呼び出した後に例外が発生します。プロテクトメソッドpublic override ServiceHostBase CreateServiceHost(string constructorString、Uri [] baseAddresses);のみを呼び出すことができます。例外は次のとおりです。例外メッセージは次のとおりです: 'ServiceHostFactory.CreateServiceHost'は現在のホスティング環境内で呼び出すことができません。このAPIでは、呼び出し側アプリケーションがIISまたはWASでホストされている必要があります。
Guy

2
@Guyサンプル問題があります。関数はprotectedMain()から自分では呼び出せないため
Andriy Drozdyuk

1
このアプローチには固有の問題があります。それは、依存関係がIISホスト環境で実際に一度だけ作成されることです。ServiceHostFactory、ServiceHost、およびInstanceProviderはすべて、アプリケーションプールがリサイクルされるまで一度だけ作成されます。つまり、依存関係を呼び出しごとに実際に更新することはできません(たとえば、DbContext)。これにより、意図しない値のキャッシュと依存関係の存続期間が長くなります。要らない。これを解決する方法が本当にわからない、何か考えはありますか?
デビッドアンダーソン、

2
@MarkSeemannただ、なぜdepすべての契約の InstanceProviderに注入したのかと思います。あなたは何ができる:ImplementedContracts.Values.First(c => c.Name == "IMyService").ContractBehaviors.Add(new MyInstanceProvider(dep));どこIMyService あなたの契約インターフェースがありますMyService(IDependency dep)。したがって、IDependency実際にそれを必要とするInstanceProviderにのみ注入します。
voytek

14

を作成してインスタンスを作成し、ServiceそのインスタンスをServiceHostオブジェクトに渡すだけです。あなたがしなければならない唯一のことは[ServiceBehaviour]、あなたのサービスのために属性を追加して、そして属性で返されたすべてのオブジェクトをマークすること[DataContract]です。

ここにモックアップがあります:

namespace Service
{
    [ServiceContract]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class MyService
    {
        private readonly IDependency _dep;

        public MyService(IDependency dep)
        {
            _dep = dep;
        }

        public MyDataObject GetData()
        {
            return _dep.GetData();
        }
    }

    [DataContract]
    public class MyDataObject
    {
        public MyDataObject(string name)
        {
            Name = name;
        }

        public string Name { get; private set; }
    }

    public interface IDependency
    {
        MyDataObject GetData();
    }
}

そして使用法:

var dep = new Dependecy();
var myService = new MyService(dep);
var host = new ServiceHost(myService);

host.Open();

これが誰かの人生を楽にしてくれることを願っています。


5
これは(で示されているようにInstanceContextMode.Single)シングルトンに対してのみ機能します。
ジョンレイノルズ

11

マークの答えIInstanceProviderは正しいです。

カスタムServiceHostFactoryを使用する代わりに、カスタム属性を使用することもできます(たとえばMyInstanceProviderBehaviorAttribute)。から派生し、実装して、次のようなメソッドをAttribute実装IServiceBehaviorしますIServiceBehavior.ApplyDispatchBehavior

// YourInstanceProvider implements IInstanceProvider
var instanceProvider = new YourInstanceProvider(<yourargs>);

foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
    foreach (var epDispatcher in dispatcher.Endpoints)
    {
        // this registers your custom IInstanceProvider
        epDispatcher.DispatchRuntime.InstanceProvider = instanceProvider;
    }
}

次に、属性をサービス実装クラスに適用します

[ServiceBehavior]
[MyInstanceProviderBehavior(<params as you want>)]
public class MyService : IMyContract

3番目のオプション:構成ファイルを使用してサービスの動作を適用することもできます。


2
技術的にはこれも解決策のように見えますが、そのアプローチでは、IInstanceProviderをサービスと密に結合します。
Mark Seemann、

2
ほんの2番目のオプション、何が良いかについての評価はありません。カスタムServiceHostFactoryを数回使用しました(特に、いくつかの動作を登録する場合)。
dalo

1
問題は、たとえば、属性コンストラクターでのみDIコンテナーを開始できることです。存在するデータを送信できません。
Guy

5

私はマークの答えから働きましたが、(少なくとも私のシナリオでは)、それは不必要に複雑でした。ServiceHostコンストラクターの1つがサービスのインスタンスを受け入れます。これは、ServiceHostFactory実装。

Markの例を便乗させると、次のようになります。

public class MyServiceHostFactory : ServiceHostFactory
{
    private readonly IDependency _dep;

    public MyServiceHostFactory()
    {
        _dep = new MyClass();
    }

    protected override ServiceHost CreateServiceHost(Type serviceType,
        Uri[] baseAddresses)
    {
        var instance = new MyService(_dep);
        return new MyServiceHost(instance, serviceType, baseAddresses);
    }
}

public class MyServiceHost : ServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}

12
これは、サービスと挿入されたすべての依存関係がスレッドセーフである場合に機能します。ServiceHostコンストラクターの特定のオーバーロードは、WCFのライフサイクル管理を本質的に無効にします。代わりに、すべての同時リクエストはによって処理されると言っていますinstance。それはパフォーマンスに影響する場合としない場合があります。同時リクエストを処理できるようにしたい場合オブジェクトグラフ全体がスレッドセーフでなければなりません。そうしないと、非決定的で不正な動作が発生します。スレッドセーフを保証できる場合、私の解決策は実際には不必要に複雑です。それを保証できない場合は、私の解決策が必要です。
Mark Seemann 2013年

3

ねじ込みます...依存性注入とサービスロケーターパターンをブレンドしました(ただし、ほとんどの場合、依存性注入であり、コンストラクターでも発生するため、読み取り専用の状態にすることができます)。

public class MyService : IMyService
{
    private readonly Dependencies _dependencies;

    // set this before creating service host. this can use your IOC container or whatever.
    // if you don't like the mutability shown here (IoC containers are usually immutable after being configured)
    // you can use some sort of write-once object
    // or more advanced approach like authenticated access
    public static Func<Dependencies> GetDependencies { get; set; }     
    public class Dependencies
    {
        // whatever your service needs here.
        public Thing1 Thing1 {get;}
        public Thing2 Thing2 {get;}

        public Dependencies(Thing1 thing1, Thing2 thing2)
        {
            Thing1 = thing1;
            Thing2 = thing2;
        }
    }

    public MyService ()
    {
        _dependencies = GetDependencies(); // this will blow up at run time in the exact same way your IoC container will if it hasn't been properly configured up front. NO DIFFERENCE
    }
}

サービスの依存関係は、ネストされたDependenciesクラスのコントラクトで明確に指定されています。IoCコンテナー(WCFの混乱をまだ修正していないコンテナー)を使用しているDependencies場合は、サービスの代わりにインスタンスを作成するように構成できます。このようにして、WCFによって課されたあまりに多くのフープを飛び越える必要がない一方で、コンテナーが与える温かみのあるあいまいな感覚を得ることができます。

私はこのアプローチで睡眠を失うつもりはありません。他の誰もすべきではない。結局のところ、あなたはIoCコンテナであり、あなたのために何かを作成するデリゲートの大きくて太い静的なコレクションです。もう1つ追加するものは何ですか?


問題の一部は、依存性注入を使用して会社を取得したいということでした。依存性注入を使用したことがないプログラマーにとってきれいで単純に見えなければ、依存性注入は他のどのプログラマーも決して使用しません。しかし、私は長年WCFを使用していないので、見逃すことはありません。
Ian Ringrose 2016年

ライトワンスプロパティへの私のアプローチは次の
とおりです。stackoverflow.com/ questions / 839788 /

0

私たちは同じ問題に直面しており、次の方法で解決しました。簡単な解決策です。

Visual Studioでは、通常のWCFサービスアプリケーションを作成し、そのインターフェイスを削除するだけです。.csファイルはそのままにして(名前を変更するだけ)、そのcsファイルを開いて、インターフェースの名前を、サービスロジックを実装する元のクラス名に置き換えます(このようにして、サービスクラスは継承を使用し、実際の実装を置き換えます)。次のように、基本クラスのコンストラクターを呼び出すデフォルトのコンストラクターを追加します。

public class Service1 : MyLogicNamespace.MyService
{
    public Service1() : base(new MyDependency1(), new MyDependency2()) {}
}

MyService基本クラスは、サービスの実際の実装です。この基本クラスには、パラメーターのないコンストラクターはなく、依存関係を受け入れるパラメーターを持つコンストラクターのみが必要です。

サービスは、元のMyServiceではなく、このクラスを使用する必要があります。

それはシンプルなソリューションであり、魅力のように機能します:-D


4
Service1をその依存関係から分離していません。これは一種のポイントでした。基本クラスなしで実行できるService1のコンストラクターで依存関係をインスタンス化したところです。
2015

0

これは非常に役立つ解決策でした-特に初心者のWCFコーダーである人にとっては。IISがホストするサービスにこれを使用している可能性のあるユーザーのために、ちょっとしたヒントを投稿したいと思いました。MyServiceHostは、ServiceHostだけでなく、WebServiceHostを継承する必要があります

public class MyServiceHost : WebServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}

これにより、IISのエンドポイントに必要なすべてのバインディングなどが作成されます。


-2

私は自分のタイプの静的変数を使用しています。これが最善の方法かどうかはわかりませんが、私にとってはうまくいきます:

public class MyServer
{   
    public static string CustomerDisplayName;
    ...
}

サービスホストをインスタンス化するとき、次のことを行います。

protected override void OnStart(string[] args)
{
    MyServer.CustomerDisplayName = "Test customer";

    ...

    selfHost = new ServiceHost(typeof(MyServer), baseAddress);

    ....
}

5
静的/シングルトンは悪です!-stackoverflow.com/questions/137975/…を
イモータルブルー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.