Inyectar datos a un servicio WCF

Tengo servicios de WCF estructurados como lo sugiere Miguel Castro . Esto significa que configuré todo de forma manual y tengo una aplicación de consola que hospeda mis servicios usando objetos ServiceHost .

Quiero mantener mis clases de servicio delgadas, y actualmente solo están transfiriendo llamadas a clases de comportamiento. Mi problema ahora es probar las unidades de las clases de servicio. Quiero inyectar algo a las clases como un parámetro constructor de modo que pueda simular esto y escribir las pruebas de unidades aisladas adecuadas. La clase ServiceHost no parece aceptar argumentos, por lo que mi pregunta es cómo puedo inyectar datos a las clases de servicio, ¿o no?

WCF admite la inyección de constructores , pero tienes que pasar por algunos aros para llegar allí. La clave está en escribir un ServiceHostFactory personalizado. Si bien eso también debe tener un constructor predeterminado, puede usarlo para conectar todos los comportamientos correctos.

Como ejemplo, recientemente escribí uno que usa Castle Windsor para conectar dependencias para la implementación del servicio. La implementación de CreateServiceHost simplemente hace esto:

return new WindsorServiceHost(this.container, serviceType, baseAddresses); 

donde this.container es un IWindsorContainer configurado.

WindsorServiceHost se ve así:

 public class WindsorServiceHost : ServiceHost { public WindsorServiceHost(IWindsorContainer container, Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses) { if (container == null) { throw new ArgumentNullException("container"); } foreach (var cd in this.ImplementedContracts.Values) { cd.Behaviors.Add(new WindsorInstanceProvider(container)); } } } 

y WindsorInstanceProvider se ve así:

 public class WindsorInstanceProvider : IInstanceProvider, IContractBehavior { private readonly IWindsorContainer container; public WindsorInstanceProvider(IWindsorContainer container) { if (container == null) { throw new ArgumentNullException("container"); } this.container = container; } #region IInstanceProvider Members public object GetInstance(InstanceContext instanceContext, Message message) { return this.GetInstance(instanceContext); } public object GetInstance(InstanceContext instanceContext) { var serviceType = instanceContext.Host.Description.ServiceType; return this.container.Resolve(serviceType); } public void ReleaseInstance(InstanceContext instanceContext, object instance) { this.container.Release(instance); } #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 } 

Esto puede parecer mucho, pero tenga en cuenta que es un código de uso general reutilizable que tiene una complejidad ciclomática bastante baja.

Puede seguir el mismo idioma de encoding para implementar la Inyección de Dependencia con otro Contenedor DI o utilizando la DI del Poor Man.

Aquí hay una versión anterior de este modismo que usa DI de Poor.

Si usaba Castle Windsor, tiene una excelente instalación de integración WCF que le permite hacer esto, y mucho más fácilmente.

¿configuró su servicio como Singleton? descubrí que las implementaciones de IInstanceProvider pueden ser problemáticas cuando se usa un contenedor DI para crear instancias de servicio.

El artículo Hosting a Mock como servicio WCF contiene un método estático que generará un host de servicio WCF basado en el objeto que se pasa al método con un solo punto final.

El método también se publicó en la respuesta para patrones recomendados para servicios web de pruebas unitarias .

Ejemplo de llamadas de uso NSustituir, pero se pueden utilizar otras funciones de simulación.