문제

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

그러나 ServiceHost를 사용하면 유형의 이름으로 제작하여 제작자에게 전달되는 주장이 아니라 생성 할 수 있습니다.

서비스 객체를 만드는 공장에서 전달할 수 있기를 원합니다.

내가 지금까지 찾은 것 :

  • WCF 의존성 주입 거동 그것은 내가 찾고있는 것 이상의 것이며 내 필요에 대해 과잉 복잡한 것 같습니다.
도움이 되었습니까?

해결책

커스텀 조합을 구현해야합니다 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 컨테이너는 이미 귀하를 위해이 작업을 수행했습니다 (Cue : Windsor의 WCF 시설).

다른 팁

당신은 단순히 당신의 인스턴스를 만들 수 있습니다 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

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

나는 Mark의 대답에서 일했지만 (적어도 시나리오에서는) 불필요하게 복잡했습니다. 중 하나 ServiceHost 생성자는 서비스의 인스턴스를 수락하며, 이는 직접 전달할 수 있습니다. 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)
    {
    }
}

나사로… 의존성 주입 및 서비스 로케이터 패턴을 혼합했습니다 (그러나 대부분 여전히 의존성 주입이며 생성자에서도 발생하여 읽기 전용 상태를 가질 수 있음).

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는 상속해야합니다 WebServiceHost, ServiceHost뿐만 아니라.

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);

    ....
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top