106

I would like to pass values into the constructor on the class that implements my service.

However ServiceHost only lets me pass in the name of the type to create, not what arguments to pass to its contrstructor.

I would like to be able to pass in a factory that creates my service object.

What I have found so far:

Elias Platek
  • 1,008
  • 9
  • 16
Ian Ringrose
  • 49,271
  • 50
  • 203
  • 302
  • 6
    I'm afraid the complexity is inherent to WCF and there isn't much you can do to alleviate it, other than not using WCF or hiding it behind more user friendly facade, like Windsor's WCF Facility if you're using Windsor – Krzysztof Kozmic Mar 16 '10 at 19:22

9 Answers9

125

You'll need to implement a combination of custom ServiceHostFactory, ServiceHost and IInstanceProvider.

Given a service with this constructor signature:

public MyService(IDependency dep)

Here's an example that can spin up 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
}

Register MyServiceHostFactory in your MyService.svc file, or use MyServiceHost directly in code for self-hosting scenarios.

You can easily generalize this approach, and in fact some DI Containers have already done this for you (cue: Windsor's WCF Facility).

piedar
  • 2,108
  • 19
  • 32
Mark Seemann
  • 209,566
  • 41
  • 390
  • 671
  • +1 (But [yuck, #regions](http://www.richard-banks.org/2011/02/anti-region-campaign.html) even though it's the least severe case of the offence, I convert to explicit interface impl myself :P) – Ruben Bartelink Jun 10 '11 at 05:52
  • 5
    How can I use it for self hosting? I receive an exception after calling to CreateServiceHost. I can only call to the protected method public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses); The exception is The exception message was: 'ServiceHostFactory.CreateServiceHost' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS. – Guy Sep 05 '12 at 08:46
  • 2
    @Guy I am having the sample problem. Because the function is `protected` I cannot call it myself from Main() – Andriy Drozdyuk Oct 11 '12 at 21:46
  • I added another answer, based on this one, that may be simpler in some cases. – McGarnagle May 10 '13 at 23:54
  • Hi, i was wondering if you could please explain the purpose of the foreach loop? foreach (var cd in this.ImplementedContracts.Values) ive tried googling implementedContracts.Values but nothing is showing up. – Hans Rudel May 23 '13 at 12:09
  • @HansRudel This is explained in further details on pp. 215-216 in my book [Dependency Injection in .NET](http://affiliate.manning.com/idevaffiliate.php?id=1150_236). – Mark Seemann May 23 '13 at 13:00
  • 1
    There is an inherent problem with this approach, and that is your dependency is really only created once in an IIS hosted environment. The ServiceHostFactory, ServiceHost, and InstanceProvider are all only created once until the application pool is recycled, which means that your dependency cannot really be refreshed per call (DbContext for example), which introduces unintended caching of values and longer lifetime of the dependency that is not wanted. I'm not really sure how to resolve this, any thoughts? – David Anderson Apr 17 '15 at 12:40
  • Just create the dependency inside the IInstanceProvider method instead. – Mark Seemann Apr 17 '15 at 13:55
  • 2
    @MarkSeemann I'm just wondering, why did you inject `dep` into every *Contract's* InstanceProvider. You could do: `ImplementedContracts.Values.First(c => c.Name == "IMyService").ContractBehaviors.Add(new MyInstanceProvider(dep));` where `IMyService ` is a contract interface of your `MyService(IDependency dep)`. So inject `IDependency` only into InstanceProvider which actually need it. – voytek Dec 02 '15 at 20:45
  • @voytek I can't remember. Perhaps that option wasn't there back then. It's 5½ years ago... – Mark Seemann Dec 02 '15 at 20:48
  • @MarkSeemann ah, could be. This answer was referenced in other one. I jumped here and this got my attention, so I asked right away. – voytek Dec 02 '15 at 20:52
  • @Guy To use in self-hosting code, create the ServiceHost manually without using the ServiceHostFactory. – piedar Aug 02 '16 at 16:27
14

You can simply create and instance of your Service and pass that instance to the ServiceHost object. The only thing you have to do is to add a [ServiceBehaviour] attribute for your service and mark all returned objects with [DataContract] attribute.

Here is a mock up:

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

and the usage:

var dep = new Dependecy();
var myService = new MyService(dep);
var host = new ServiceHost(myService);

host.Open();

I hope this will make life easier for someone.

kerim
  • 215
  • 2
  • 6
11

Mark's answer with the IInstanceProvider is correct.

Instead of using the custom ServiceHostFactory you could also use a custom attribute (say MyInstanceProviderBehaviorAttribute). Derive it from Attribute, make it implement IServiceBehavior and implement the IServiceBehavior.ApplyDispatchBehavior method like

// 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;
    }
}

Then, apply the attribute to your service implementation class

[ServiceBehavior]
[MyInstanceProviderBehavior(<params as you want>)]
public class MyService : IMyContract

The third option: you can also apply a service behavior using the configuration file.

Sam Holder
  • 30,911
  • 13
  • 94
  • 171
dalo
  • 458
  • 4
  • 8
  • 2
    Technically, this also looks like a solution, but with that approach, you tightly couple the IInstanceProvider with the service. – Mark Seemann Mar 16 '10 at 14:48
  • 2
    Just a second option, no assessment on what's better. I have used the custom ServiceHostFactory a couple of times (especially when you want to register several behaviors). – dalo Mar 16 '10 at 14:59
  • 1
    The problem is that you can initiate for example DI container only in the attribute constructor.. you can't send exist data. – Guy Sep 05 '12 at 08:43
5

I worked from Mark's answer, but (for my scenario at least), it was needlessly complex. One of the ServiceHost constructors accepts an instance of the service, which you can pass in directly from the ServiceHostFactory implementation.

To piggyback off Mark's example, it would look like this:

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)
    {
    }
}
McGarnagle
  • 96,448
  • 30
  • 213
  • 255
  • 12
    This will work if your service and all injected dependencies are thread-safe. That particular overload of the ServiceHost constructor essentially disables WCF's lifecycle management. Instead, you're saying that *all* concurrent requests will be handled by `instance`. That may or may not impact performance. If you want to be able to handle concurrent requests, that *entire* object graph must be thread-safe, or you will get non-deterministic, incorrect behaviour. If you can guarantee thread-safety, my solution is, indeed, needlessly complex. If you can't guarantee that, my solution is required. – Mark Seemann May 12 '13 at 12:02
4

Screw it… I blended the dependency injection and service locator patterns (but mostly it's still dependency injection and it even takes place in the constructor which means you can have read-only state).

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
    }
}

The dependencies of the service are clearly specified in the contract of it's nested Dependencies class. If you are using an IoC container (one that doesn't already fix the WCF mess for you), you can configure it to create the Dependencies instance instead of the service. This way you get the warm fuzzy feeling that your container gives you while also not having to jump through too many hoops imposed by WCF.

I'm not going to lose any sleep over this approach. Neither should anyone else. After all, you're IoC container is a big, fat, static collection of delegates that creates stuff for you. What's adding one more?

Ronnie Overby
  • 41,852
  • 68
  • 257
  • 338
  • Part of the problem was that I wish to get the company using dependency injection, and if it did not look clean and simple to a programmer that had never used dependency injection, then dependency injection would never be used by any other programmer. However I have not used WCF for many years, and I don't miss it! – Ian Ringrose Oct 19 '16 at 10:02
  • Here's my approach to a write-once property http://stackoverflow.com/questions/839788/is-there-a-way-of-setting-a-property-once-only-in-c-sharp/40209216#40209216 – Ronnie Overby Oct 24 '16 at 15:11
1

This was a very helpful solution - especially for someone who is a novice WCF coder. I did want to post a little tip for any users who might be using this for an IIS-hosted service. MyServiceHost needs to inherit WebServiceHost, not just ServiceHost.

public class MyServiceHost : WebServiceHost
{
    public MyServiceHost(MyService instance, Type serviceType, params Uri[] baseAddresses)
        : base(instance, baseAddresses)
    {
    }
}

This will create all the necessary bindings, etc for your endpoints in IIS.

Eric Dieckman
  • 73
  • 1
  • 4
0

We were facing this same problem and have solved it in the following manner. It is a simple solution.

In Visual Studio just create a normal WCF service application and remove it's interface. Leave the .cs file in place (just rename it) and open that cs file and replace the name of the interface with your original class name which implements the service logic (this way the service class uses inheritance and replaces your actual implementation). Add a default constructor that calls the base class's constructors, like this:

public class Service1 : MyLogicNamespace.MyService
{
    public Service1() : base(new MyDependency1(), new MyDependency2()) {}
}

The MyService base class is the actual implementation of the service. This base class should not have a parameterless constructor, but only constructors with parameters that accept the dependencies.

The service should use this class instead of the original MyService.

It's a simple solution and works like a charm :-D

Ron Deijkers
  • 1,625
  • 1
  • 20
  • 26
  • 4
    You haven't decoupled Service1 from its dependencies, which was kind of the point. You've just instantiated the dependencies in the constructor for Service1, which you can do without the base class. – saille Apr 30 '15 at 23:07
0

Create your instanced service with its dependencies (let's call it myService), then open up your ServiceHost like so:

var myService = new Service(argumentOne, argumentTwo, . . . etc.);
var host = new WebServiceHost(myService, new Uri("http://localhost:80"));
var behavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
behavior.InstanceContextMode = InstanceContextMode.Single;
host.Open();
codeMonkey
  • 2,788
  • 2
  • 25
  • 41
-2

I use static variables of my type. Not sure if this is the best way, but it works for me:

public class MyServer
{   
    public static string CustomerDisplayName;
    ...
}

When I instantiate service host I do the following:

protected override void OnStart(string[] args)
{
    MyServer.CustomerDisplayName = "Test customer";

    ...

    selfHost = new ServiceHost(typeof(MyServer), baseAddress);

    ....
}
Boris
  • 13
  • 1