2

I'm trying to inject a some business services in a WCF service. I read this really interessting post: How do I pass values to the constructor on my wcf service?

And I have done the following:

Custom ServiceHost

    public class UnityServiceHost : ServiceHost
    {
        public UnityServiceHost(IUnityContainer container, Type serviceType, params Uri[] baseAddresses)
            : base(serviceType, baseAddresses)
        {
            if (container == null)
            {
                throw new ArgumentNullException(nameof(container));
            }

            foreach (ContractDescription contractDescription in ImplementedContracts.Values)
            {
                contractDescription.Behaviors.Add(new UnityInstanceProvider(serviceType, container));
            }
        }
    }

Custom Instance Provider

public class UnityInstanceProvider : IInstanceProvider, IContractBehavior
{
    private readonly Type m_serviceType;
    private readonly IUnityContainer m_container;

    public UnityInstanceProvider(Type serviceType, IUnityContainer container)
    {
        m_serviceType = serviceType;
        m_container = container;
    }
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return GetInstance(instanceContext);
    }
    public object GetInstance(InstanceContext instanceContext)
    {
        return m_container.Resolve(m_serviceType);
    }
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        (instance as IDisposable)?.Dispose();
    }
    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)     {       }
}

The service behavior that I've is looking like this:

[ServiceBehavior] 
public class MyService : IMyService 
{
    public ServerInformationService(ISomeDependency coreManager)
    {
        //...
    }
}

But I don't get into the GetInstance, and when I run the code, I get this:

The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.

What have I done wrong? It look like my instance provider isn't even used

J4N
  • 15,713
  • 29
  • 136
  • 260
  • This indicates that the dependency that you are trying to inject should have a parameter-less constructor. – ArunGeorge Jul 26 '17 at 12:06
  • @ArunGeorge I don't think so, it event doesn't stop on the `return m_container.Resolve(m_serviceType);` breakpoint, I'm able to ask for the same exact service with the dependencies outside of WPF. – J4N Jul 26 '17 at 12:09
  • Is your service hosted in IIS? Are you using the `UnityServiceHost` class? – Yacoub Massad Jul 26 '17 at 15:46
  • @YacoubMassad No, it's selfhosted. I finally found the issue. I was using ContractBehavior when I should have used ServiceBehavior. I'm confirming that everything is working and I will publish an answer – J4N Jul 27 '17 at 04:54

1 Answers1

1

I finally found the solution. I was applying my InstanceProvider to Contact and not to services.

Here is my final (Working) solution:

public class UnityServiceHost : ServiceHost
{
    public UnityServiceHost(IUnityContainer container, Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
        if (container == null)
        {
            throw new ArgumentNullException(nameof(container));
        }
        Description.Behaviors.Add(new UnityInstanceProvider(serviceType, container));
    }
}

And the behavior+instance provider:

public class UnityInstanceProvider : IInstanceProvider, IServiceBehavior
{
    private readonly Type m_serviceType;
    private readonly IUnityContainer m_container;

    public UnityInstanceProvider(Type serviceType, IUnityContainer container)
    {
        m_serviceType = serviceType;
        m_container = container;
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher cd = (ChannelDispatcher)channelDispatcherBase;
            foreach (EndpointDispatcher ed in cd.Endpoints)
            {
                if (!ed.IsSystemEndpoint)
                {
                    ed.DispatchRuntime.InstanceProvider = this;
                }
            }
        }
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return m_container.Resolve(m_serviceType);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return GetInstance(instanceContext);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        (instance as IDisposable)?.Dispose();
    }
}

It works because I'm self-hosting the service(new UnityServiceHost(...) ...., if it was on IIS, I could not create the behavior that way

J4N
  • 15,713
  • 29
  • 136
  • 260