C#WCF插件的设计与实现

C#WCF插件的设计与实现,c#,wcf,plugins,mef,C#,Wcf,Plugins,Mef,我想得到一些建议。我正在开发一个系统,该系统将在运行时加载插件,并要求它们通过WCF端点可用 我将有一个MVC3Web应用程序,它只用于配置,还有一个类库(core),可以加载不同的插件 我希望您能为我提供一些指导。我想加载插件,然后能够创建一个WCF端点,该端点已向IIS7注册,以便访问该插件 提前感谢:)使用功的导数,你可以实现你想要的东西。让我们从一个我们可能想要托管的示例服务开始,我们将其称为IMessageBroker,它的契约很简单: [ServiceContract] public

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

我将有一个MVC3Web应用程序,它只用于配置,还有一个类库(core),可以加载不同的插件

我希望您能为我提供一些指导。我想加载插件,然后能够创建一个WCF端点,该端点已向IIS7注册,以便访问该插件

提前感谢:)

使用功的导数,你可以实现你想要的东西。让我们从一个我们可能想要托管的示例服务开始,我们将其称为
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服务,我们需要创建一个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;
    }
}
我们正在做的是将对
/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
上动态访问它。您可能想要做的是提供一个静态服务,它允许您对可用的端点进行交互,并将其反馈给您的消费客户端

哦,别忘了连接虚拟路径提供程序:

HostingEnvironment.RegisterVirtualPathProvider(new ServiceVirtualPathProvider());

你找到了MEF的插件,对吗?每个插件实现一个特定的接口?你所说的“创建一个在IIS 7中注册的WCF端点以访问该插件”到底是什么意思?我最后做的是在实际的插件中,定义一个WCF联系人和所有工作,并为插件启动一个端点。理想情况下,我们想要做的是尝试将该端点注册到IIS7中。但现在看来我会按照我现在的方向去做。你可以一直自己主持,这会让你最终控制发现的端点。谢谢Matthew,我会试试的。我的解决方案最终略有相似,但这是一个更好的实现
[Export(typeof(IMessageBroker)),
 ExportMetadata("Name", "SampleMessageBroker"),
 ExportMetadata("Channel", "Greetings")]
public class SampleMessageBroker : IMessagerBroker
{
  public string Send(string message)
  {
    return "Hello! " + message;
  }
}
HostingEnvironment.RegisterVirtualPathProvider(new ServiceVirtualPathProvider());