サービスを実装するクラスのコンストラクターに値を渡したいのですが。
ただし、ServiceHostでは、作成する型の名前のみを渡すことができます。コンストラクタに渡す引数は渡されません。
サービスオブジェクトを作成するファクトリを渡すことができるようにしたいと思います。
これまでに見つけたもの:
- WCF Dependency Injection Behaviorは、私が探している以上のものであり、私のニーズに対して複雑すぎるようです。
サービスを実装するクラスのコンストラクターに値を渡したいのですが。
ただし、ServiceHostでは、作成する型の名前のみを渡すことができます。コンストラクタに渡す引数は渡されません。
サービスオブジェクトを作成するファクトリを渡すことができるようにしたいと思います。
これまでに見つけたもの:
回答:
カスタムの組み合わせを実装する必要がありますServiceHostFactory
、ServiceHost
と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機能)。
protected
Main()から自分では呼び出せないため
dep
すべての契約の InstanceProviderに注入したのかと思います。あなたは何ができる:ImplementedContracts.Values.First(c => c.Name == "IMyService").ContractBehaviors.Add(new MyInstanceProvider(dep));
どこIMyService
あなたの契約インターフェースがありますMyService(IDependency dep)
。したがって、IDependency
実際にそれを必要とするInstanceProviderにのみ注入します。
を作成してインスタンスを作成し、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();
これが誰かの人生を楽にしてくれることを願っています。
InstanceContextMode.Single
)シングルトンに対してのみ機能します。
マークの答え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番目のオプション:構成ファイルを使用してサービスの動作を適用することもできます。
私はマークの答えから働きましたが、(少なくとも私のシナリオでは)、それは不必要に複雑でした。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)
{
}
}
instance
。それはパフォーマンスに影響する場合としない場合があります。同時リクエストを処理できるようにしたい場合、オブジェクトグラフ全体がスレッドセーフでなければなりません。そうしないと、非決定的で不正な動作が発生します。スレッドセーフを保証できる場合、私の解決策は実際には不必要に複雑です。それを保証できない場合は、私の解決策が必要です。
ねじ込みます...依存性注入とサービスロケーターパターンをブレンドしました(ただし、ほとんどの場合、依存性注入であり、コンストラクターでも発生するため、読み取り専用の状態にすることができます)。
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つ追加するものは何ですか?
私たちは同じ問題に直面しており、次の方法で解決しました。簡単な解決策です。
Visual Studioでは、通常のWCFサービスアプリケーションを作成し、そのインターフェイスを削除するだけです。.csファイルはそのままにして(名前を変更するだけ)、そのcsファイルを開いて、インターフェースの名前を、サービスロジックを実装する元のクラス名に置き換えます(このようにして、サービスクラスは継承を使用し、実際の実装を置き換えます)。次のように、基本クラスのコンストラクターを呼び出すデフォルトのコンストラクターを追加します。
public class Service1 : MyLogicNamespace.MyService
{
public Service1() : base(new MyDependency1(), new MyDependency2()) {}
}
MyService基本クラスは、サービスの実際の実装です。この基本クラスには、パラメーターのないコンストラクターはなく、依存関係を受け入れるパラメーターを持つコンストラクターのみが必要です。
サービスは、元のMyServiceではなく、このクラスを使用する必要があります。
それはシンプルなソリューションであり、魅力のように機能します:-D
これは非常に役立つ解決策でした-特に初心者のWCFコーダーである人にとっては。IISがホストするサービスにこれを使用している可能性のあるユーザーのために、ちょっとしたヒントを投稿したいと思いました。MyServiceHostは、ServiceHostだけでなく、WebServiceHostを継承する必要があります。
public class MyServiceHost : WebServiceHost
{
public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
: base(instance, baseAddresses)
{
}
}
これにより、IISのエンドポイントに必要なすべてのバインディングなどが作成されます。
私は自分のタイプの静的変数を使用しています。これが最善の方法かどうかはわかりませんが、私にとってはうまくいきます:
public class MyServer
{
public static string CustomerDisplayName;
...
}
サービスホストをインスタンス化するとき、次のことを行います。
protected override void OnStart(string[] args)
{
MyServer.CustomerDisplayName = "Test customer";
...
selfHost = new ServiceHost(typeof(MyServer), baseAddress);
....
}