C# WCF插件设计与实现

2024-03-11

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

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

我希望得到一些有关如何解决此问题的指导。我想加载该插件,然后能够创建一个向 IIS 7 注册的 WCF 端点以访问该插件。

提前致谢 :)


使用导数Darko 的动态 IIS 托管 WCF 服务 http://blog.micic.ch/net/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; }
}

由于这是一个简单的项目,我将作弊并使用一个简单的静态类来管理 MEFCompositionContainer用于组成零件:

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。然后我们生成一些标记,它代表一个标记.svc文件与Service="SampleMessageBroker"。该参数将被传递给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());
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# WCF插件设计与实现 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • 为什么 int8_t 和用户通过 cin 输入显示奇怪的结果[重复]

    这个问题在这里已经有答案了 一小段代码让我发疯 但希望你能阻止我跳出窗外 看这里 include
  • 如何让 Swagger 插件在自托管服务堆栈中工作

    我已经用 github 上提供的示例重新提出了这个问题 并为任何想要自己运行代码的人提供了一个下拉框下载链接 Swagger 无法在自托管 ServiceStack 服务上工作 https stackoverflow com questio
  • 计算 Richtextbox 中所有单词的最有效方法是什么?

    我正在编写一个文本编辑器 需要提供实时字数统计 现在我正在使用这个扩展方法 public static int WordCount this string s s s TrimEnd if String IsNullOrEmpty s re
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • 从 Linux 内核模块中调用用户空间函数

    我正在编写一个简单的 Linux 字符设备驱动程序 以通过 I O 端口将数据输出到硬件 我有一个执行浮点运算的函数来计算硬件的正确输出 不幸的是 这意味着我需要将此函数保留在用户空间中 因为 Linux 内核不能很好地处理浮点运算 这是设
  • 标准化 UTF-8 到底是什么?

    The 重症监护室项目 http userguide icu project org transforms normalization 现在也有一个PHP库 http us php net manual en class normalize
  • DbContext 和 ObjectContext 有什么区别

    From MSDN 表示工作单元和存储库模式的组合 使您能够查询数据库并将更改分组在一起 然后将这些更改作为一个单元写回存储 DbContext在概念上类似于ObjectContext 我虽然DbContext只处理与数据库的连接以及针对数
  • 如何在 32 位或 64 位配置中以编程方式运行任何 CPU .NET 可执行文件?

    我有一个可在 32 位和 64 位处理器上运行的 C 应用程序 我试图枚举给定系统上所有进程的模块 当尝试从 64 位应用程序枚举 32 位进程模块时 这会出现问题 Windows 或 NET 禁止它 我认为如果我可以从应用程序内部重新启动
  • “接口”类似于 boost::bind 的语义

    我希望能够将 Java 的接口语义与 C 结合起来 起初 我用过boost signal为给定事件回调显式注册的成员函数 这非常有效 但后来我发现一些函数回调池是相关的 因此将它们抽象出来并立即注册所有实例的相关回调是有意义的 但我了解到的
  • C++ 函数重载类似转换

    我收到一个错误 指出两个重载具有相似的转换 我尝试了太多的事情 但没有任何帮助 这是那段代码 CString GetInput int numberOfInput BOOL clearBuffer FALSE UINT timeout IN
  • 按 Esc 按键关闭 Ajax Modal 弹出窗口

    我已经使用 Ajax 显示了一个面板弹出窗口 我要做的是当用户按 Esc 键时关闭该窗口 这可能吗 如果有人知道这一点或以前做过这一点 请帮助我 Thanks 通过以下链接 您可以通过按退出按钮轻松关闭窗口 http www codepro
  • 无法接收 UDP Windows RT

    我正在为 Windows 8 RT 编写一个 Windows Store Metro Modern RT 应用程序 需要在端口 49030 上接收 UDP 数据包 但我似乎无法接收任何数据包 我已按照使用教程进行操作DatagramSock
  • 我的班级应该订阅自己的公共活动吗?

    我正在使用 C 3 0 遵循标准事件模式我有 public event EventHandler
  • Oracle Data Provider for .NET 不支持 Oracle 19.0.48.0.0

    我们刚刚升级到 Oracle 19c 19 3 0 所有应用程序都停止工作并出现以下错误消息 Oracle Data Provider for NET 不支持 Oracle 19 0 48 0 0 我将 Oracle ManagedData
  • 使用 .NET Process.Start 运行时挂起进程 - 出了什么问题?

    我在 svn exe 周围编写了一个快速而肮脏的包装器来检索一些内容并对其执行某些操作 但对于某些输入 它偶尔会重复挂起并且无法完成 例如 一个调用是 svn list svn list http myserver 84 svn Docum
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看
  • 当从finally中抛出异常时,Catch块不会被评估

    出现这个问题的原因是之前在 NET 4 0 中运行的代码在 NET 4 5 中因未处理的异常而失败 部分原因是 try finallys 如果您想了解详细信息 请阅读更多内容微软连接 https connect microsoft com
  • 从列表中选择项目以求和

    我有一个包含数值的项目列表 我需要使用这些项目求和 我需要你的帮助来构建这样的算法 下面是一个用 C 编写的示例 描述了我的问题 int sum 21 List
  • 如何将 PostgreSql 与 EntityFramework 6.0.2 集成? [复制]

    这个问题在这里已经有答案了 我收到以下错误 实体框架提供程序类型的 实例 成员 Npgsql NpgsqlServices Npgsql 版本 2 0 14 2 文化 中性 PublicKeyToken 5d8b90d52f46fda7 没

随机推荐