2011-04-27 10 views
7

Me gustaría obtener algunos consejos. Estoy desarrollando un sistema que cargará complementos en tiempo de ejecución y requerirá que estén disponibles a través de un punto final WCF.C# WCF plugin diseño e implementación

Tendré una aplicación web MVC 3 que solo se usa realmente para la configuración, y una biblioteca de clases (núcleo) que cargará diferentes complementos.

Agradecería alguna orientación sobre cómo hacerlo. Me gustaría cargar el complemento y luego poder crear un punto final WCF que esté registrado con IIS 7 para acceder a ese complemento.

Gracias de antemano :)

+0

Así que usted está encontrando con los plugins MEF correcta? ¿Cada complemento implementa una interfaz específica?¿A qué se refiere exactamente con "crear un punto final WCF que esté registrado con IIS 7 para acceder a ese complemento"? – BrandonZeider

+0

Bueno, lo que terminé haciendo es en el complemento actual, definir un contacto WCF y todos los trabajos y poner en marcha un punto final para el complemento. Lo que idealmente queríamos hacer era intentar registrar ese punto final en IIS 7. Pero parece que seguiré con la forma en que me dirijo en este momento. –

+0

Siempre puede auto-hospedarse, eso le daría el control final sobre los puntos finales descubiertos. – BrandonZeider

Respuesta

11

El uso de un derivado de Darko's Dynamic IIS hosted WCF Service trabajo, se puede lograr algo lo que quiere. Vamos a empezar con un servicio de ejemplo lo que se quiere acoger, lo llamaremos un IMessageBroker, es un contrato es simple:

[ServiceContract] 
public interface IMessageBroker 
{ 
    [OperationContract] 
    string Send(string message); 
} 

Utilizamos este contrato, tanto para el servicio, y las exportaciones/importaciones de MEF. También vamos a definir algunos metadatos adicionales para ir junto con él:

public interface IMessageBrokerMetadata 
{ 
    public string Name { get; } 
    public string Channel { get; } 
} 

Como se trata de un proyecto simple, voy a engañar y utilizar una clase estática sencilla para la gestión del MEF CompositionContainer utilizado para componer partes:

public static class MEF 
{ 
    private static CompositionContainer container; 
    private static bool initialised; 

    public static void Initialise() 
    { 
     var catalog = new DirectoryCatalog("bin"); 
     container = new CompositionContainer(catalog); 
     initialised = true; 
    } 

    public static CompositionContainer Container 
    { 
     get 
     { 
      if (!initialised) Initialise(); 
      return container; 
     } 
    } 
} 

para poder generar servicios WCF de forma dinámica, tenemos que crear un ServiceHostFactory que pueden acceder a nuestro contenedor de composición para acceder a nuestros tipos, por lo que podríamos hacer:

public class MEFServiceHostFactory : ServiceHostFactory 
{ 
    public override ServiceHostBase CreateServiceHost(string constructorString, System.Uri[] baseAddresses) 
    { 
     var serviceType = MEF.Container 
      .GetExports<IMessageBroker, IMessageBrokerMetadata>() 
      .Where(l => l.Metadata.Name == constructorString) 
      .Select(l => l.Value.GetType()) 
      .Single(); 

     var host = new ServiceHost(serviceType, baseAddresses); 

     foreach (var contract in serviceType.GetInterfaces()) 
     { 
      var attr = contract.GetCustomAttributes(typeof(ServiceContractAttribute), true).FirstOrDefault(); 
      if (attr != null) 
       host.AddServiceEndpoint(contract, new BasicHttpBinding(), ""); 
     } 

     var metadata = host.Description.Behaviors 
      .OfType<ServiceMetadataBehavior>() 
      .FirstOrDefault(); 

     if (metadata == null) 
     { 
      metadata = new ServiceMetadataBehavior(); 
      metadata.HttpGetEnabled = true; 
      host.Description.Behaviors.Add(metadata); 
     } 
     else 
     { 
      metadata.HttpGetEnabled = true; 
     } 

     return host; 
    } 
} 

Esencialmente, el argumento constructorString se usa para pasar el nombre de Metadata que queremos para el servicio específico. A continuación, tenemos que manejar la ubicación de estos servicios. Lo que ahora necesitamos es un VirtualPathProvider que podamos usar para crear dinámicamente la instancia, a través de un VirtualFile. El proveedor se vería así:

public class ServiceVirtualPathProvider : VirtualPathProvider 
{ 
    private bool IsServiceCall(string virtualPath) 
    { 
     virtualPath = VirtualPathUtility.ToAppRelative(virtualPath); 
     return (virtualPath.ToLower().StartsWith("~/services/")); 
    } 

    public override VirtualFile GetFile(string virtualPath) 
    { 
     return IsServiceCall(virtualPath) 
        ? new ServiceFile(virtualPath) 
        : Previous.GetFile(virtualPath); 
    } 

    public override bool FileExists(string virtualPath) 
    { 
     if (IsServiceCall(virtualPath)) 
      return true; 

     return Previous.FileExists(virtualPath); 
    } 

    public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart) 
    { 
     return IsServiceCall(virtualPath) 
        ? null 
        : Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); 
    } 
} 

Lo que estamos haciendo, es el mapeo de todas las llamadas a /Services/ a nuestros criterios de valoración MEF derivados. El servicio necesita un archivo virtual, y aquí es donde atamos todo junto:

public class ServiceFile : VirtualFile 
{ 
    public ServiceFile(string virtualPath) : base(virtualPath) 
    { 

    } 

    public string GetName(string virtualPath) 
    { 
     string filename = virtualPath.Substring(virtualPath.LastIndexOf("/") + 1); 
     filename = filename.Substring(0, filename.LastIndexOf(".")); 

     return filename; 
    } 

    public override Stream Open() 
    { 
     var stream = new MemoryStream(); 
     var writer = new StreamWriter(stream); 

     writer.Write("<%@ ServiceHost Language=\"C#\" Debug=\"true\" Service=\"" + GetName(VirtualPath) + 
        "\" Factory=\"Core.MEFServiceHostFactory, Core\" %>"); 
     writer.Flush(); 

     stream.Position = 0; 
     return stream; 
    } 
} 

el archivo virtual a romper con el nombre de metadatos de la ruta de acceso virtual, donde /Services/SampleMessageBroker.svc ->SampleMessageBroker. A continuación, generamos un marcado que representa el marcado de un archivo .svc con Service="SampleMessageBroker". Este argumento se pasará al MEFServiceHostFactory donde podemos seleccionar puntos finales. Por lo tanto, teniendo en cuenta un criterio de valoración de la muestra:

[Export(typeof(IMessageBroker)), 
ExportMetadata("Name", "SampleMessageBroker"), 
ExportMetadata("Channel", "Greetings")] 
public class SampleMessageBroker : IMessagerBroker 
{ 
    public string Send(string message) 
    { 
    return "Hello! " + message; 
    } 
} 

Ahora podemos acceder a esa dinámica en /Services/SampleMessageBroker.svc. Lo que podría querer hacer es proporcionar un servicio estático que le permita integrar qué puntos finales están disponibles y retroalimentarlos a sus clientes consumidores.

Oh, no hay que olvidar que cablear su proveedor de ruta de acceso virtual:

HostingEnvironment.RegisterVirtualPathProvider(new ServiceVirtualPathProvider()); 
+0

gracias Matthew voy a dar una oportunidad. Mi solución ha terminado siendo ligeramente similar, pero esta es una implementación mucho mejor –

Cuestiones relacionadas