2011-04-27 53 views
7

我想获得一些建议。我正在开发一个系统,它将在运行时加载插件,并要求它们通过WCF端点可用。C#WCF插件设计与实现

我将有一个MVC 3 Web应用程序仅用于配置,而一个类库(核心)将加载不同的插件。

我将不胜感激关于如何去做这个的一些指导。我想加载插件,然后能够创建一个WCF端点,该端点通过IIS 7注册以访问该插件。

感谢提前:)

+0

所以你发现MEF的插件是否正确?每个插件实现一个特定的接口?通过“创建一个注册到IIS 7以访问该插件的WCF端点”,你到底意味着什么? – BrandonZeider 2011-04-27 15:33:14

+0

那么我最终做的是在实际的插件中,定义一个WCF联系人和所有的作品,并为插件启动一个端点。我们理想的做法是尝试将该端点注册到IIS 7中。但看起来我会按照目前的方式进行操作。 – 2011-04-28 14:09:57

+0

你可以自己主持,这会让你最终控制发现的端点。 – BrandonZeider 2011-04-28 14:23:26

回答

11

使用Darko's Dynamic IIS hosted WCF Service工作的衍生物,可以达到你想要的东西。让我们先从我们可能要举办一个示例服务,我们把它叫做一个IMessageBroker,它的合同很简单:

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

我们使用这个合同无论是服务,而MEF出口/进口。我们还将定义了一些额外的元数据与它一起去:

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

由于这是一个简单的项目,我会欺骗和使用一个简单的静态类,用于管理组成部分的MEF CompositionContainer

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; 
     } 
    } 
} 

为了能够动态生成WCF服务,我们需要创建一个可以访问我们的组合容器来访问我们的A型ServiceHostFactory,所以你可以这样做:

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; 
    } 
} 

实质上,constructorString参数用于传递特定服务所需的元数据名称。接下来,我们需要处理这些服务的定位。我们现在需要的是VirtualPathProvider,我们可以通过VirtualFile来动态创建实例。该供应商将是这样的:

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); 
    } 
} 

我们正在做的,被映射到/Services/我们MEF来源的端点的呼叫。该服务需要一个虚拟文件,而这正是我们将其结合在一起:

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; 
    } 
} 

虚拟文件会从虚拟路径,其中/Services/SampleMessageBroker.svc打出来的元数据的名称 - >SampleMessageBroker。然后,我们生成一些标记,它代表Service="SampleMessageBroker"代表.svc文件的标记。这个参数将被传递给MEFServiceHostFactory,我们可以选择端点。所以,对于一个样本端点:

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

我们现在可以访问动态的/Services/SampleMessageBroker.svc。你可能想要做的是提供一个静态服务,它允许你将可用的端点进行集成,并将其反馈给消费客户端。

哦,别忘了要连接的虚拟路径提供:

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

感谢马修,我会放弃这一切。我的解决方案最终有点类似,但这是一个更好的实现 – 2011-04-29 06:05:42