WCF and Unity: Set up InstanceProvider, ServiceBehaviour, -Host, -Factory... Now what?
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:
- Integrating Unity with WCF
- WCF and Unity 2.0
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 diff开发者_如何学运维erent filename perhaps?
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" %>
...
精彩评论