내 wcf 서비스의 생성자에 값을 어떻게 전달합니까?


103

내 서비스를 구현하는 클래스의 생성자에 값을 전달하고 싶습니다.

그러나 ServiceHost는 생성자에게 전달할 인수가 아닌 생성 할 유형의 이름 만 전달할 수 있습니다.

내 서비스 개체를 만드는 팩토리를 전달할 수 있기를 원합니다.

지금까지 내가 찾은 것 :


6
WCF에 내재 된 복잡성이 두렵기 때문에 WCF를 사용하지 않거나 Windsor
Krzysztof Kozmic을

답변:


122

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 시설).


+1 (그러나 윽, #regions 는 범죄의 가장 심각한 사건에도 불구하고, 나는 나 자신 IMPL 명시 적 인터페이스로 변환 : P)
루벤 Bartelink

5
셀프 호스팅에 어떻게 사용할 수 있습니까? CreateServiceHost를 호출 한 후 예외가 발생합니다. 보호 된 메서드 만 호출 할 수 있습니다. public override ServiceHostBase CreateServiceHost (string constructorString, Uri [] baseAddresses); 예외는 다음과 같습니다. 예외 메시지 : 'ServiceHostFactory.CreateServiceHost'는 현재 호스팅 환경 내에서 호출 할 수 없습니다. 이 API를 사용하려면 호출 애플리케이션이 IIS 또는 WAS에서 호스팅되어야합니다.
가이

2
@Guy 샘플 문제가 있습니다. 기능이 있기 때문에 protected나는 홈페이지 ()에서 자신을 호출 할 수 없습니다
안드리 Drozdyuk

1
이 접근 방식에는 내재 된 문제가 있으며 이는 종속성이 실제로 IIS 호스팅 환경에서 한 번만 생성된다는 것입니다. ServiceHostFactory, ServiceHost 및 InstanceProvider는 모두 응용 프로그램 풀이 재활용 될 때까지 한 번만 생성됩니다. 즉, 호출 당 종속성을 실제로 새로 고칠 수 없습니다 (예 : DbContext). 즉, 의도하지 않은 값 캐싱과 종속성의 수명이 더 길어집니다. 원하지 않습니다. 이 문제를 해결하는 방법을 잘 모르겠습니다.
David Anderson

2
@MarkSeemann 왜 dep모든 계약의 InstanceProvider에 주입했는지 궁금 합니다. 당신은 할 수 : ImplementedContracts.Values.First(c => c.Name == "IMyService").ContractBehaviors.Add(new MyInstanceProvider(dep));어디에 IMyService 당신의 계약 인터페이스입니다 MyService(IDependency dep). 따라서 IDependency실제로 필요한 InstanceProvider에만 주입 하십시오.
voytek 2015

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

세 번째 옵션 : 구성 파일을 사용하여 서비스 동작을 적용 할 수도 있습니다.


2
기술적으로 이것은 또한 솔루션처럼 보이지만이 접근 방식을 사용하면 IInstanceProvider를 서비스와 밀접하게 연결합니다.
Mark Seemann

2
두 번째 옵션이며 더 나은 것에 대한 평가는 없습니다. 사용자 지정 ServiceHostFactory를 두 번 사용했습니다 (특히 여러 동작을 등록하려는 경우).
dalo

1
문제는 속성 생성자에서만 DI 컨테이너를 시작할 수 있다는 것입니다. 기존 데이터를 보낼 수 없습니다.
가이

5

나는 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)
    {
    }
}

12
서비스 및 삽입 된 모든 종속성이 스레드로부터 안전하면 작동합니다. ServiceHost 생성자의 특정 오버로드는 기본적으로 WCF의 수명주기 관리를 비활성화합니다. 대신 모든 동시 요청이 instance. 이는 성능에 영향을 미칠 수도 있고 그렇지 않을 수도 있습니다. 동시 요청을 처리 할 수 ​​있으려면 전체 개체 그래프가 스레드로부터 안전해야합니다. 그렇지 않으면 비결정적이고 잘못된 동작이 발생합니다. 스레드 안전성을 보장 할 수 있다면 내 솔루션은 실제로 불필요하게 복잡합니다. 보장 할 수 없다면 내 솔루션이 필요합니다.
Mark Seemann

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 컨테이너가 당신을 위해 무언가를 생성하는 델리게이트의 크고 뚱뚱하고 정적 컬렉션입니다. 하나 더 추가하는 것은 무엇입니까?


문제의 일부는 종속성 주입을 사용하여 회사를 얻고 싶다는 것이며, 종속성 주입을 사용하지 않은 프로그래머에게 깨끗하고 단순 해 보이지 않으면 다른 프로그래머가 종속성 주입을 사용하지 않을 것입니다. 그러나 나는 수년 동안 WCF를 사용하지 않았고 그것을 놓치지 않습니다!
Ian Ringrose

여기에 추 기형의 속성에 대한 나의 접근 방식 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의 생성자에서 종속성을 인스턴스화했습니다. 기본 클래스없이 수행 할 수 있습니다.
saille 2015-04-30

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.