C#WCF插件的设计和实现

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

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

我会很感激如何解决这个问题。 我想加载插件,然后能够创建一个在IIS 7中注册的WCF端点,以访问该插件。

提前致谢 :)

使用Darko动态IIS托管的WCF服务工作的衍生产品,您可以实现您想要的function。 让我们从我们可能想要托管的示例服务开始,我们将其称为IMessageBroker ,它的合同很简单:

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

我们将此合同用于服务和MEF出口/import。 我们还将定义一些额外的元数据:

 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服务,我们需要创建一个ServiceHostFactory,它可以访问我们的组合容器来访问我们的类型,所以你可以这样做:

 public class MEFServiceHostFactory : ServiceHostFactory { public override ServiceHostBase CreateServiceHost(string constructorString, System.Uri[] baseAddresses) { var serviceType = MEF.Container .GetExports() .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() .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());