C# – Passing parameters to a WCF ServiceHost type with Ninject 2

cninjectwcf

I want to use a Ninject.Wcf extension to create a parametrized service host instance.

For example I have a class MyWCFHandler with the only following constructor:

public MyWCFHandler(UserManager manager)
{
  _manager = manager;
}

But when I write var myServiceHost = new ServiceHost(typeof(MyWCFHandler)); I have no way to pass the dependency object to a constructor.

I don't want to mess with the custom ServiceHost like offered in
How do I pass values to the constructor on my wcf service?

I decided to go with the Ninject way, but not able to fully understand how to act in my situation.

Here is what I understand WCF Extension in Ninject works:

  1. Reference Ninject and Ninject.Extensions.WCF in my project.
  2. Create a class that inherits Ninject module and write something like:

    internal class ServiceModule : NinjectModule
    {
    public override void Load()
    {
    Bind<IUserManager>().To<UserManager>().WithConstructorParameters(myUserManagerIwantToUseInWCFHandler);
    }
    }

  3. Add a Kernel initialized with new ServiceModule() to a KernelContainer.

  4. Use the NinjectServiceHost like this:

    var service = KernelContainer.Kernel.Get<IMyWCFHandler>();
    _host = new NinjectServiceHost( service );

And there I should have my host ready to be opened.

The questions are:

How should I pass my constructor parameters into a NinjectModule? Should I create an instance of a NinjectModule right when I am ready to bind a parameter to it? How do I pass them to Get method?

Unfortunately there is not one example around to simple show the parametrized ServiceHost start. I don't even care if it is Ninject I use. Whichever solution has a good example – I am fine with it, since I am just deciding what IoC container to use.

Best Answer

Regarding ninject. The answer is it depends whether you want a singleton service or a new instance per request. With a singleton service you can do the following:

public class TimeServiceModule : NinjectModule
{
    /// <summary>
    /// Loads the module into the kernel.
    /// </summary>
    public override void Load()
    {
        this.Bind<ITimeService>().To<TimeService>();

        this.Bind<ServiceHost>().ToMethod(ctx => ctx.Kernel.Get<NinjectServiceHost>(new ConstructorArgument("singletonInstance", c => c.Kernel.Get<ITimeService>())));
    }
}

internal static class Program
{
    private static void Main()
    {
        var kernel = new StandardKernel(new TimeServiceModule());

        var serviceHost = kernel.Get<ServiceHost>();
        serviceHost.AddServiceEndpoint(typeof(ITimeService), new NetTcpBinding(), "net.tcp://localhost/TimeService");
        try
        {
            serviceHost.Open();
        }
        finally
        {
            serviceHost.Close();
        }
    }
}

Per request approach:

public interface IServiceTypeProvider
{
    /// <summary>
    /// Gets the service types.
    /// </summary>
    /// <value>The service types.</value>
    IEnumerable<Type> Types { get; }
}

Func<Type, ServiceHost> serviceHostFactory

        foreach (Type serviceType in this.ServiceTypeProvider.Types)
        {
            // I do some magic here to query base contracts because all our service implement a marker interface. But you don't need this. But then you might need to extend the type provider interface.
            IEnumerable<Type> contracts = QueryBaseContracts(serviceType );

            var host = this.CreateHost(serviceType);

            foreach (Type contract in contracts)
            {
                Binding binding = this.CreateBinding();
                string address = this.CreateEndpointAddress(contract);

                this.AddServiceEndpoint(host, contract, binding, address);
            }

            host.Description.Behaviors.Add(new ServiceFacadeBehavior());

            this.OpenHost(host);

            this.serviceHosts.Add(host);
        }

    protected virtual ServiceHost CreateHost(Type serviceType )
    {
        return this.serviceHostFactory(serviceType );
    }

public class YourWcfModule : NinjectModule
{
    /// <summary>
    /// Loads the module into the kernel.
    /// </summary>
    public override void Load()
    {

        this.Bind<Func<Type, ServiceHost>>().ToMethod(
            ctx =>
            (serviceType) =>
            ctx.Kernel.Get<NinjectServiceHost>(new ConstructorArgument("serviceType", serviceType), new ConstructorArgument("baseAddresses", new Uri[] { })));
    }
}

Have fun

Related Topic