2011-01-19 7 views
5

Estoy intentando agregar compatibilidad con IoC a mi servicio REST WCF (Windows Server 2008). Soy nuevo en esto y estoy siguiendo las instrucciones proporcionadas en el vídeo a continuación:Intentando agregar compatibilidad con la IoC a los servicios de WCF

http://www.dimecasts.net/Content/WatchEpisode/150

El vídeo guía a través de una serie de clases que me ayude a conseguir de StructureMap COI en funcionamiento, mientras que la exposición de los puntos finales de WCF. He publicado todo el código al final de esta publicación.

Cuando ejecuto mi código, el StructureMapServiceHost clase personalizada genera un error @ el StructureMapServiceHost (tipoServicio Tipo, params Uri [] baseAddress) Método:

public class StructureMapServiceHost : ServiceHost 
{ 
    public StructureMapServiceHost() {} 

    public StructureMapServiceHost(Type serviceType, params Uri[] baseAddress) 
     : base(serviceType, baseAddress) 
    { 

    } 

    protected override void OnOpening() 
    { 
     Description.Behaviors.Add(new IoCServiceBehavior()); 
     base.OnOpening(); 
    } 
} 

me están dijo que:

El tipo de servicio proporcionado no se pudo cargar como un servicio porque no tiene un constructor predeterminado (sin parámetros). Para solucionar el problema, agregue un constructor predeterminado al tipo o pase una instancia del tipo al host.

Esto es cierto, no es así. Pero el ejemplo de video tampoco tenía ninguno. Debajo está mi servicio:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] 
    public class UserService : IUserService 
    { 
     public UserService(IUserRepository specification) 
     { 
      Specification = specification; 
     } 

     public List<User> GetAllUsers() 
     { 
      return Specification.GetAllUsers(); 
     } 

     public User GetUser(string userId) 
     { 
      return Specification.GetUserById(new Guid(userId)); 
     } 

     private List<User> SearchForUsers(string searchString) 
     { 
      return Specification.SearchUsers(searchString); 
     } 

     public IUserRepository Specification { get; set; } 

    } 



public class IoCServiceBehavior : IServiceBehavior 
    { 
     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 cdb in serviceHostBase.ChannelDispatchers) 
      { 
       new StructureMapInstanceProvider(serviceDescription.ServiceType); 
      } 
     } 
    } 


public class StructureMapInstanceProvider : IInstanceProvider 
{ 
    private readonly Type _serviceType; 

    public StructureMapInstanceProvider(Type serviceType) 
    { 
     _serviceType = serviceType; 
    } 

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

    public object GetInstance(InstanceContext instanceContext, Message message) 
    { 
     var instance = ObjectFactory.GetInstance(_serviceType); 

     return instance; 
    } 

    public void ReleaseInstance(InstanceContext instanceContext, object instance) 
    { 
     throw new NotImplementedException(); 
    } 
} 




public class StructureMapServiceHostFactory : ServiceHostFactory 
    { 
     public StructureMapServiceHostFactory() 
     { 
      IoCBootstrap.SetupIoc(); 
     } 

     protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
     { 
      return new StructureMapServiceHost(serviceType, baseAddresses); 
     } 
    } 

¿Alguna idea? Gracias.

EDITAR * ** * ** * ** * ** * ** * ** * ** * ** * * * * ****

De StructureMapServiceHost Eliminé:

public StructureMapServiceHost(Type serviceType, params Uri[] baseAddress) 
      : base(serviceType, baseAddress) { } 

y ha añadido:

public StructureMapServiceHost(Object singletonInstance, params Uri[] baseAddress) 
      : base(singletonInstance, baseAddress) { } 

y luego se retira el parámetro de mi constructor UserService. No estoy recibiendo el error:

The HTML document does not contain Web service discovery information.

Respuesta

0

no puedo dar un vistazo al vídeo en este momento (limitaciones de Internet), pero estoy bastante seguro de la clase en su ejemplo no tenía ningún constructor en todos. Y en ese caso, el compilador genera un constructor sin parámetros vacío en su nombre. Por lo tanto, su clase hizo tener un constructor predeterminado después de todo.

En cuanto a la excepción, que parece bastante sencillo: el constructor sin parámetros no inicializa la propiedad Specification, por lo que siempre null - que, naturalmente, provoca una NullReferenceException una vez que se intenta acceder a él en sus métodos.

Parece que lo que pretendía aquí es crear usted mismo el objeto UserService y pasarle un IUserRepository, ¿verdad?(O, tal vez, utilizar el marco COI para que?)

En ese caso, es mejor utilizar la sobrecarga de constructor ServiceHost 's que toma un object en lugar de Type. De esta forma, tendrá control total sobre su objeto UserService, y no necesitará un constructor predeterminado en absoluto.

+0

Gracias Fedor. He intentado lo que sugirió y obtuve un nuevo error. Por favor mira mi EDIT arriba. ¿Puedes decirme qué estoy haciendo mal? La ilustración del código podría ayudar en este punto ... muy apreciada. –

+0

@Code Sherpa: intente acceder a ese servicio con su navegador y vea qué error le da. –

+0

Hola Fydor - Estoy obteniendo: El recurso no se puede encontrar. Descripción: HTTP 404. El recurso que está buscando (o una de sus dependencias) podría haberse eliminado, haber cambiado su nombre o no estar disponible temporalmente. Revise la siguiente URL y asegúrese de que esté escrita correctamente. URL solicitada: /Users.svc/users –

17

Su servicio utiliza InstanceContextMode.SingleCall y el equipo WCF han decidido, en su infinita sabiduría, que cuando el InstanceContextMode es SingleCall la IInstanceProvider no se invoca para crear la instancia (Ver http://blogs.msdn.com/b/carlosfigueira/archive/2011/05/31/wcf-extensibility-iinstanceprovider.aspx - segundo párrafo a continuación interfaz título declration).

Currrently tengo menos de forma ideal de conseguir alrededor de eso en la fábrica de host de servicio:

using System; 
using System.Collections.Generic; 
using System.ServiceModel; 
using System.ServiceModel.Activation; 
using StructureMap; 
using StructureMap.Pipeline; 
using System.Linq; 

using ServiceHostCreator = System.Func<System.Type, System.Uri[], System.ServiceModel.ServiceHost>; 

namespace x.ServiceExtensions 
{ 
    public class xWebServiceHostFactory : ServiceHostFactory 
    { 
     private readonly IDictionary<InstanceContextMode, ServiceHostCreator> _serviceHostCreators; 

     public xWebServiceHostFactory() 
     { 
      ObjectFactory.Initialize(init => 
             init.Scan(scan => 
                { 
                 scan.AssembliesFromApplicationBaseDirectory(); 
                 scan.IgnoreStructureMapAttributes(); 
                 scan.LookForRegistries(); 
                })); 
      _serviceHostCreators = new Dictionary<InstanceContextMode, ServiceHostCreator> 
             { 
              { InstanceContextMode.PerCall, (t, a) => PerCallServiceHostCreator(t, a) }, 
              { InstanceContextMode.PerSession, (t, a) => PerSessionServiceHostCreator(t, a) }, 
              { InstanceContextMode.Single, (t, a) => SingleInstanceServiceHostCreator(t, a) } 
             }; 
     } 

     protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
     { 
      var serviceInstanceContextMode = GetServiceInstanceContextMode(serviceType); 
      var serviceHostCreator = _serviceHostCreators[ serviceInstanceContextMode ]; 
      return serviceHostCreator(serviceType, baseAddresses); 
     } 

     private static InstanceContextMode GetServiceInstanceContextMode(Type serviceType) 
     { 
      var serviceBehaviour = serviceType 
       .GetCustomAttributes(typeof (ServiceBehaviorAttribute), true) 
       .Cast<ServiceBehaviorAttribute>() 
       .SingleOrDefault(); 
      return serviceBehaviour.InstanceContextMode; 
     } 

     private static ServiceHost PerCallServiceHostCreator(Type serviceType, Uri[] baseAddresses) 
     { 
      var args = new ExplicitArguments(); 
      args.Set(serviceType); 
      args.Set(baseAddresses); 
      var serviceHost = ObjectFactory.GetInstance<TelaWebServiceHost>(args); 
      return serviceHost; 
     } 

     private static ServiceHost PerSessionServiceHostCreator(Type serviceType, Uri[] baseAddresses) 
     { 
      return PerCallServiceHostCreator(serviceType, baseAddresses); 
     } 

     private static ServiceHost SingleInstanceServiceHostCreator(Type serviceType, Uri[] baseAddresses) 
     { 
      var service = ObjectFactory.GetInstance(serviceType); 
      var args = new ExplicitArguments(); 
      args.Set(typeof(object), service); 
      args.Set(baseAddresses); 
      var serviceHost = ObjectFactory.GetInstance<TelaWebServiceHost>(args); 
      return serviceHost; 
     } 
    } 
} 

Este es un trabajo en progreso y puede haber una mejor manera, pero por el momento no puedo t encuentra uno.

+1

Gracias por mencionar la información altamente relevante sobre InstanceContextMode, me estaba causando un gran dolor y ninguno de los muchos artículos que había leído lo mencionaba. Creo que en el caso donde necesite usar Single para el modo, entonces debe usar el constructor ServiceHost que acepta un parámetro de instancia singleton y pasar en una instancia resuelta. – Shaun

0

Para cualquiera que esté tratando de hacer algo así, actualmente recomiendo utilizar Spring.NET como contenedor IoC. Si bien puede no ser tan fácil de usar como algunos otros contenedores (no me gusta especialmente su configuración XML), tiene la mejor integración de WCF. También viene con una solución inteligente y transparente para el problema InstanceContextMode.SingleCall (utilizando su marco AOP/proxy dinámico).

http://www.springframework.net/docs/1.2.0-M1/reference/html/wcf.html

http://www.springframework.net/doc-latest/reference/html/wcf-quickstart.html

Cuestiones relacionadas