내 서비스를 구현하는 클래스의 생성자에 값을 전달하고 싶습니다.
그러나 ServiceHost는 생성자에게 전달할 인수가 아닌 생성 할 유형의 이름 만 전달할 수 있습니다.
내 서비스 개체를 만드는 팩토리를 전달할 수 있기를 원합니다.
지금까지 내가 찾은 것 :
- WCF 종속성 주입 동작 은 내가 찾고있는 것보다 더 많고 내 요구에 대해 지나치게 복잡해 보입니다.
내 서비스를 구현하는 클래스의 생성자에 값을 전달하고 싶습니다.
그러나 ServiceHost는 생성자에게 전달할 인수가 아닌 생성 할 유형의 이름 만 전달할 수 있습니다.
내 서비스 개체를 만드는 팩토리를 전달할 수 있기를 원합니다.
지금까지 내가 찾은 것 :
답변:
custom 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
나는 홈페이지 ()에서 자신을 호출 할 수 없습니다
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
세 번째 옵션 : 구성 파일을 사용하여 서비스 동작을 적용 할 수도 있습니다.
나는 Mark의 대답에서 일했지만 (적어도 내 시나리오에서는) 불필요하게 복잡했습니다. ServiceHost
생성자 중 하나 는 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 컨테이너가 당신을 위해 무언가를 생성하는 델리게이트의 크고 뚱뚱하고 정적 컬렉션입니다. 하나 더 추가하는 것은 무엇입니까?
우리는이 같은 문제에 직면했고 다음과 같은 방식으로 해결했습니다. 간단한 해결책입니다.
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);
....
}