サービスを実装するクラスのコンストラクターに値を渡したいと思います。
ただし、ServiceHost では、コンストラクターに渡す引数ではなく、作成する型の名前のみを渡すことができます。
サービス オブジェクトを作成するファクトリを渡すことができるようにしたいと考えています。
私がこれまでに見つけたもの:
- WCF Dependency Injection Behaviorは、私が探している以上のものであり、私のニーズに対して複雑すぎるようです。
サービスを実装するクラスのコンストラクターに値を渡したいと思います。
ただし、ServiceHost では、コンストラクターに渡す引数ではなく、作成する型の名前のみを渡すことができます。
サービス オブジェクトを作成するファクトリを渡すことができるようにしたいと考えています。
私がこれまでに見つけたもの:
ServiceHostFactory
customとの組み合わせを実装する必要があり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's WCF Facility)。
のインスタンスを作成して、そのインスタンスをオブジェクト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();
これが誰かの人生を楽にすることを願っています。
マークの答え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 番目のオプション: 構成ファイルを使用してサービスの動作を適用することもできます。
くそったれ… 依存性注入とサービス ロケーター パターンをブレンドしました (ただし、ほとんどは依存性注入であり、コンストラクターでも実行されるため、読み取り専用状態にすることができます)。
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 コーダーにとって非常に役立つ解決策でした。IIS がホストするサービスでこれを使用している可能性のあるユーザーのために、ちょっとしたヒントを投稿したいと思いました。MyServiceHost はServiceHost だけでなく、WebServiceHostを継承する必要があります。
public class MyServiceHost : WebServiceHost
{
public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
: base(instance, baseAddresses)
{
}
}
これにより、IIS のエンドポイントに必要なすべてのバインディングなどが作成されます。
この同じ問題に直面しており、次の方法で解決しました。これは簡単な解決策です。
Visual Studio では、通常の WCF サービス アプリケーションを作成し、そのインターフェイスを削除するだけです。.cs ファイルをそのままにして (名前を変更するだけ)、その cs ファイルを開き、インターフェイスの名前を、サービス ロジックを実装する元のクラス名に置き換えます (このようにして、サービス クラスは継承を使用し、実際の実装を置き換えます)。次のように、基本クラスのコンストラクターを呼び出す既定のコンストラクターを追加します。
public class Service1 : MyLogicNamespace.MyService
{
public Service1() : base(new MyDependency1(), new MyDependency2()) {}
}
MyService 基本クラスは、サービスの実際の実装です。この基本クラスには、パラメーターなしのコンストラクターを含めることはできませんが、依存関係を受け入れるパラメーターを持つコンストラクターのみを含める必要があります。
サービスは、元の MyService の代わりにこのクラスを使用する必要があります。
これはシンプルなソリューションであり、魅力のように機能します:-D