1

I have been looking for a way to use Unity for Dependency Injection in my WCF service. I have been trying to understand the code described in these two blogs, which is quite similar:

So I added this code to a separate project in my solution and refer to the custom servicehostfactory in a SVC file to my WFC service (separate project).

The question is now: How do I access the objects in my container from my WCF Service methods?


EDIT

These are my implementations:

ServiceHostFactory...

namespace UnityWcfAssembler
{
public class UnityServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        UnityServiceHost serviceHost = new UnityServiceHost(serviceType, baseAddresses);
        UnityContainer container = new UnityContainer();
        serviceHost.Container = container;

        //TODO configuration from app.config 
        //configure container
        //UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
        //section.Configure(serviceHost.Container);

        InitializeSessionFactories(container);

        return serviceHost;
    }

    private static void InitializeSessionFactories(UnityContainer container)
    {
        Dictionary<String, ISessionFactory> sessions = new Dictionary<string, ISessionFactory>();

        Configuration Cfg = new Configuration();
        Cfg.Configure();
        Cfg.SetProperty("connection.connection_string",
            "Data Source=(Local);Initial Catalog=Fossils;Integrated Security=true;");
        ISessionFactory Factory = Cfg.BuildSessionFactory();
        sessions.Add("fossils", Factory);

        Cfg.SetProperty("connection.connection_string",
            "Data Source=(Local);Initial Catalog=TypeCollection;Integrated Security=true;");
        ISessionFactory typeFactory = Cfg.BuildSessionFactory();

        sessions.Add("type", typeFactory);

        Cfg.SetProperty("connection.connection_string",
            "Data Source=(Local);Initial Catalog=PersonalCollection;Integrated Security=true;");
        ISessionFactory persFactory = Cfg.BuildSessionFactory();

        sessions.Add("personal", persFactory);

        container.RegisterInstance(sessions);
    }
}
}

ServiceHost...

namespace UnityWcfAssembler
{
    public class UnityServiceHost : ServiceHost
    {
        public UnityContainer Container { get; set; }

        public UnityServiceHost()
        {
            Container = new UnityContainer();
        }

        public UnityServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
        {
            Container = new UnityContainer();
        }

        protected override void OnOpening()
        {
            new UnityServiceBehavior(Container).AddToHost(this);

            base.OnOpening();

            if (Description.Behaviors.Find<UnityServiceBehavior>() == null)
                Description.Behaviors.Add(new UnityServiceBehavior(Container));
        }
    }
}

InstanceProvider...

namespace UnityWcfAssembler
{
    public class UnityInstanceProvider : IInstanceProvider
    {
        public UnityContainer Container { set; get; }
        public Type ServiceType { set; get; }

        public UnityInstanceProvider() : this(null)
        {
        }

        public UnityInstanceProvider(Type type)
        {
            ServiceType = type;
            Container = new UnityContainer();
        }

        // Get Service instace via unity container
        public object GetInstance(InstanceContext instanceContext, Message message)
        {
            return Container.Resolve(ServiceType);
        }

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

        public void ReleaseInstance(InstanceContext instanceContext, object instance)
        {
        }
    }
}

IServiceBehavior...

namespace UnityWcfAssembler
{
    public class UnityServiceBehavior : IServiceBehavior
    {
        public UnityInstanceProvider InstanceProvider { get; set; }
        private ServiceHost serviceHost;

        public UnityServiceBehavior()
        {
            InstanceProvider = new UnityInstanceProvider();
        }

        public UnityServiceBehavior(UnityContainer unity)
        {
            InstanceProvider = new UnityInstanceProvider();
            InstanceProvider.Container = unity;
        }

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

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

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

        public void AddToHost(ServiceHost host)
        {
            // only add to host once
            if (serviceHost != null) return;
            host.Description.Behaviors.Add(this);
            serviceHost = host;
        }
    }
}

Wcf Service...

namespace FossilsWcfService
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] 
    public class FossilsService : IFossilsService
    {
        private readonly Dictionary<string, ISessionFactory> sessionFactories;

        public FossilsService(Dictionary<string, ISessionFactory> s)
        {
            sessionFactories = s;
        }

        public SpeciesList GetAllSpecies()
        {
            SpeciesList list = new SpeciesList();

            ISessionFactory factory = sessionFactories["fossils"];

            if(factory == null)
            {
                list.Species.Add(new FossilSpecies { GenusName = "Session factory could not be resolved from container!" });
                return list;                
            }

            ISession session = factory.OpenSession();

            SpeciesManager speciesManager = new SpeciesManager(session);

            IList<FossilSpecies> species = speciesManager.GetAllSpecies();

            foreach (FossilSpecies fossilSpecies in species)
            {
                list.Species.Add(fossilSpecies);
            }
            return list;
        }

FossilsWcfService.svc...

<%@ ServiceHost Language="C#" Debug="true" 
Service="Server.Services.ExampleService"
Factory="UnityWcfAssembler.UnityServiceHostFactory" %>

Should the latter have a different filename perhaps?

Fedor Alexander Steeman
  • 1,551
  • 3
  • 23
  • 43

1 Answers1

3

Now that you have the factory working, you should create a property with the interface, and declare the constructor and Unity will do the rest

public class InvoiceService : IInvoiceService {

    private IPayService payService;

    public IPayService PayService
    {
        get { return payService; }
        set { payService= value; }
    }


    public InvoiceService(IPayService provider)
    {
        this.payService= provider;
    }

    public bool Pay(){
         return PayService.Pay();
    }

}

My Service Factory Implementaion

public class InvoiceFactory : ServiceHostFactory
    {
        protected override ServiceHost CreateServiceHost(
                                          Type serviceType, Uri[] baseAddresses)
        {
            UnityServiceHost host = new UnityServiceHost(serviceType, baseAddresses);
            UnityContainer unity = new UnityContainer();
            host.Container = unity;

//I'm doing it like this because I put some AOP in the service injected
            var clazz = Intercept.ThroughProxy<IPayService>(new PayServiceConcreteClass(), 
                                            new InterfaceInterceptor(), new[] { new LoggingInjection() });

            unity.RegisterType<IPayService>().RegisterInstance(clazz);

            return host;
        }
    }

EDIT after code in the question

I'm not really sure what could be wrong, but I spot two things that I'm not sure about:

ServiceHostFactory...

//Better as an Interface
IDictionary<String, ISessionFactory> sessions = new Dictionary<string, ISessionFactory>();


//container.RegisterInstance(sessions);
//Registering the type not the class
container.RegisterType<IDictionary<String, ISessionFactory>>().RegisterInstance(sessions);

Wcf Service...

private readonly IDictionary<string, ISessionFactory> sessionFactories;

        public FossilsService(IDictionary<string, ISessionFactory> s)
        {
            sessionFactories = s;
        }

FossilsWcfService.svc

It's not missing the codebehind attribute? something like this

<%@ ServiceHost Language="C#" Debug="true" Factory="InvoiceFactory" Service="InvoiceService" CodeBehind="InvoiceService.svc.cs" %>

...

jjchiw
  • 4,018
  • 27
  • 30
  • Thanks for the quick reply. I do believe I am doing that. However, if I have do not add a parameterless constructor, the WCF host throws an error complaining about the lack of it. But if I add it, it is using that and not the constructor providing my object. – Fedor Alexander Steeman Aug 19 '11 at 10:15
  • You have to jump through some hoops if you want it to use your custom constructors instead of the default one. See the following questions: http://stackoverflow.com/questions/381831/can-wcf-service-have-constructors and http://stackoverflow.com/questions/2454850/how-do-i-pass-values-to-the-constructor-on-my-wcf-service/2455039#2455039. – Christophe Geers Aug 19 '11 at 10:20
  • The second link that @cgeer points http://stackoverflow.com/questions/2454850/how-do-i-pass-values-to-the-constructor-on-my-wcf-service/2455039#2455039 is the "same" as the second one you linked in the question http://blogs.microsoft.co.il/blogs/gadib/archive/2010/11/30/wcf-and-unity-2-0.aspx you have to create implement all the interfaces and the service factory, it's in the link you posted – jjchiw Aug 19 '11 at 10:26
  • This is the follow up of the post of Ray Henry http://initializecomponent.blogspot.com/2008/06/unity-wcf-and-iis.html – jjchiw Aug 19 '11 at 10:32
  • Thanks everyone. I *did* read all those blog posts and implemented all those interfaces. I put these in a separate project. The solution at http://stackoverflow.com/questions/2454850/how-do-i-pass-values-to-the-constructor-on-my-wcf-service/2455039#2455039 is different from the blog posts in that a dependency is passed as a parameter to the ServiceHost. In the blog posts, the container is simply initiated and configured/populated in the ServiceHostFactory. I am missing something here, but not sure what. I tried to copy the ServiceHostFactory to my WCF Service project to see if that would help. – Fedor Alexander Steeman Aug 19 '11 at 11:15
  • Here is another blog post: http://weblogs.asp.net/jdanforth/archive/2008/12/19/wcf-unity-and-nhibernate-first-findings.aspx that leans on the same example. The service is automagically instantiated with the dependency without itself referring to the container. How can I get my service to do that? – Fedor Alexander Steeman Aug 19 '11 at 12:13
  • Can you paste the implementation of the factory and the interfaces? – jjchiw Aug 19 '11 at 12:21
  • Thanks. I am not sure how the RegisterType-method is to be formed here exactly. Your suggested form there is not understood by Visual Studio. Besides, I need to register the instance as it's chopful of data, not just the type. – Fedor Alexander Steeman Aug 22 '11 at 05:46
  • "typo" it should be like this container.RegisterType>().RegisterInstance(sessions); updated in the code – jjchiw Aug 22 '11 at 07:40
  • About the parameterless constructor error, here it's explainend why http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html?showComment=1180743000000#c2470898961849407587 – jjchiw Aug 22 '11 at 07:49
  • Thanks. On top of everything, it turned out my project was badly constructed. It started out as a WCF library but I had been adding stuff like the svc manually, without changing it into a WCF application. I now build it up from scratch. Did some of your suggestions, and presto! The only thing that doesn't work is using IDictionary instead of Dictionary. Otherwise it is finally working! – Fedor Alexander Steeman Aug 22 '11 at 09:11