【问题标题】:C# WCF plugin design and implementationC# WCF插件设计与实现
【发布时间】:2011-04-27 08:43:16
【问题描述】:

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

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

我希望能提供一些有关如何进行此操作的指导。我想加载插件,然后能够创建一个向 IIS 7 注册的 WCF 端点,以便访问该插件。

提前致谢:)

【问题讨论】:

  • 那么您找到的 MEF 插件正确吗?每个插件实现一个特定的接口? “创建一个向 IIS 7 注册以访问该插件的 WCF 端点”到底是什么意思?
  • 好吧,我最终做的是在实际插件中,定义一个 WCF 联系人和所有工作,并为插件启动一个端点。我们最理想的做法是尝试将该端点注册到 IIS 7 中。但看起来我会按照我目前的方式前进。
  • 您始终可以自行托管,这将使您能够最终控制已发现的端点。

标签: c# wcf plugins mef


【解决方案1】:

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

为了能够动态生成 W​​CF 服务,我们需要创建一个可以访问我们的组合容器以访问我们的类型的 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());

【讨论】:

  • 谢谢马修,我会试一试的。我的解决方案最终有点相似,但这是一个更好的实现
猜你喜欢
  • 2012-05-29
  • 2011-04-15
  • 2011-07-16
  • 2013-10-10
  • 1970-01-01
  • 1970-01-01
  • 2016-12-12
  • 1970-01-01
相关资源
最近更新 更多