C#WCF插件设计与实现 [英] C# WCF plugin design and implementation

查看:49
本文介绍了C#WCF插件设计与实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to get some advice. I am developing a system that will load up plugins at runtime and require them to be available through a WCF endpoint.

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

I will have a MVC 3 Web app that is only really used for configuration, and a class library (core) that will load up different plugins.

我希望您能获得一些有关如何实现此目标的指导.我想加载插件,然后能够创建在IIS 7中注册的WCF终结点,以访问该插件.

I would appreciate some guidance on how to go about this. I would like to load the plugin up and then be able to create a WCF endpoint that is registered with IIS 7 for access into that plugin.

先谢谢您了:)

推荐答案

使用 Darko的动态IIS托管WCF服务工作,您可以实现所需的目标.让我们从一个我们可能希望托管的示例服务开始,我们将其称为IMessageBroker,它的合同很简单:

Using a derivative of Darko's Dynamic IIS hosted WCF Service work, you can achieve something what you want. Let's start with an example service we might want to host, we'll call it an IMessageBroker, it's contract is simple:

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

我们将本合同用于服务和MEF出口/进口.我们还将定义一些附加的元数据来配合它:

We use this contract for both the Service, and the MEF Exports/Imports. We'll also define some additional metadata to go along with it:

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

因为它是一个简单的项目,所以我作弊并使用一个简单的静态类来管理用于组成零件的MEF CompositionContainer:

As it's a simple project, I'll cheat and use a simple static class for managing the MEF CompositionContainer used to compose parts:

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,可以访问我们的组合容器以访问我们的类型,因此您可以:

To be able to generate WCF Services dynamically, we need to create a ServiceHostFactory that can access our composition container to access our types, so you could do:

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动态创建实例.提供程序看起来像:

Essentially the constructorString argument is used to pass in the Metadata name we want for the specific service. Next up, we need to handle locating these services. What we now need is a VirtualPathProvider which we can use to dynamically create the instance, through a VirtualFile. The provider would look like:

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派生的终结点.该服务需要一个虚拟文件,这是我们将所有文件捆绑在一起的地方:

What we are doing, is mapping any calls to /Services/ to our MEF derived endpoints. The service needs a virtual file, and this is where we tie it all together:

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)中分解出元数据名称.然后,我们生成一些标记,这些标记表示.svc文件与Service="SampleMessageBroker"的标记.此参数将传递给MEFServiceHostFactory,我们可以在其中选择端点.因此,给定一个示例端点:

The virtual file will break out the Metadata name from the virtual path, where /Services/SampleMessageBroker.svc -> SampleMessageBroker. We then generate some markup which represents the markup of an .svc file with Service="SampleMessageBroker". This argument will be passed to the MEFServiceHostFactory where we can select out endpoints. So, given a sample endpoint:

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

我们现在可以在/Services/SampleMessageBroker.svc上动态访问它.您可能想做的是提供一个静态服务,该服务可让您检查可用的端点,并将其反馈给使用客户.

We can now access that dynamically at /Services/SampleMessageBroker.svc. What you might want to do, is provide a static service which allows you to interegate what endpoints are available, and feed that back to your consuming clients.

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

Oh, don't forget to wire up your virtual path provider:

HostingEnvironment.RegisterVirtualPathProvider(new ServiceVirtualPathProvider());

这篇关于C#WCF插件设计与实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆