web-dev-qa-db-ja.com

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

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

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

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

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

102
Ian Ringrose

カスタムServiceHostFactoryServiceHost、およびIInstanceProviderの組み合わせを実装する必要があります。

このコンストラクターシグネチャを持つサービスを考えます:

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機能)。

119
Mark Seemann

単に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();

これにより誰かの生活が楽になることを願っています。

13
kerim

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

11
dalo

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

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

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)
    {
    }
}
5
McGarnagle

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

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つ追加しているのは何ですか?

3
Ronnie Overby

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

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

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

0
Eric Dieckman

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

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

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

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

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

それは簡単な解決策であり、魅力のように機能します:-D

0
Ron Deijkers