从现有 API 自动生成服务器端 WCF 服务

Posted

技术标签:

【中文标题】从现有 API 自动生成服务器端 WCF 服务【英文标题】:Generate Server Side WCF Service automatically from existing API 【发布时间】:2015-09-09 23:09:49 【问题描述】:

一个人如何在不使用 WCF 项目的情况下通过 WCF 公开由多个类组成的 API 的每个方法。

例如,假设我有以下

public interface RainfallMonitor

    [ExposeToWeb]
    void RecordRainfall(string county, float rainfallInches);

    [ExposeToWeb]
    float GetTotalRainfall(string county);

    void ClearRainfall(string county);

我知道我可以像往常一样创建一个 WCF 服务库,然后添加一个名为“RainfallMonitor”的 WCF 服务。

我正在探索的是......是否有可能/合理地在编译时为整个 API 生成所有与 WCF 相关的代码,而不实际使类 WCF 服务。可能使用 ExposeToWeb 等属性来表示通过服务公开哪些方法。结果将像这样运行:

    在名为 RainfallAPI 的项目中创建/修改类 编译并自动生成另一个名为RainfallService 的项目/dll。

基本上:

如果这是可能的,我可以采取什么方法来实际实施 它? 我会遇到哪些严重的陷阱? 是否有任何现有的代码库可以做类似的事情,我可以从中寻找灵感

澄清一下:我不是在问关于自动生成客户端存根,我是在问关于在服务器端创建服务。

【问题讨论】:

我能想到两个潜在的问题:1) 方法重载不能 1:1 映射到多个 OperationContracts(名称必须不同)。 2) API 中使用的所有复杂类型都必须是可序列化的(例如 [DataContracts]),因此 API 作者必须意识到他们的 API 将作为 WCF 服务公开。 【参考方案1】:

我最近是这个图书馆的负责人:Fody。据我了解,它可以挂钩到构建过程并将 IL 注入程序集。我不完全确定它是如何工作的,但可以通过 IL 进行搜索,找到所有具有 ExposeToWeb 属性的方法,并使用它来将 WCF 服务的合同发送到程序集中。

但另一方面,如果您已经在类中添加属性,为什么不首先添加正确的 WFC 属性,然后使用SvcUtil 在构建后生成合同?

编辑: 这是一个如何使用svcutil的示例:

C#:

[ServiceContract]
public interface IRainfallMonitor

    [OperationContract]
    void RecordRainfall(string county, float rainfallInches);


public class RainfallMonitor : IRainfallMonitor

    public void RecordRainfall(string county, float rainfallInches)
    
        // code
    

构建后 PowerShell:

$svcutil = "C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\SvcUtil.exe"
$csc = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe"
$assembly = "bin/debug/ProjectWithoutWCF.dll"
$service = "ProjectWithoutWCF.RainfallMonitor"
$outputns = "ProjectWithoutWCF.RainfallMonitor.Service"
$outputdir = "bin/debug"

md svcutil_tmp
cd svcutil_tmp

& $svcutil /serviceName:"$service" "../$assembly"
& $svcutil *.wsdl *.xsd /importxmltypes /out:"output.cs" /n:"*,$outputns"
& $csc /target:library /out:$outputns.dll "output.cs"

cp "$outputns.dll" "../$outputdir"
cp output.config "../$outputdir/$outputns.dll.config"
cd ..
rm -r .\svcutil_tmp

您将在项目配置中需要这样的东西:

<system.serviceModel>
  <services>
    <service name="ProjectWithoutWCF.RainfallMonitor" >
      <endpoint address="" binding="basicHttpBinding" contract="ProjectWithoutWCF.IRainfallMonitor">
      </endpoint>
    </service>
  </services>
</system.serviceModel>

它有点繁琐,您很可能需要对脚本和配置进行一些调整。但结果是您有一个带有 WCF 服务合同的 ProjectWithoutWCF.RainfallMonitor.Service.dll 文件。

【讨论】:

【参考方案2】:

对于您关于“我正在探索的是......是否有可能/合理地在编译时为整个 API 生成所有与 WCF 相关的代码而不实际使类 WCF 服务。”,有选项,但它们都需要大量工作。

您需要编写一个代码生成应用程序/库,该应用程序/库利用诸如CSharpCodeProvider 之类的类(VB 也有一个)和反射来检查您的库作为构建后步骤,构建您想要的代码内存,并将其保存为 DLL。

此应用程序需要找到您创建的自定义属性,表明它应该是 WCF 服务,并根据您定义的规则输出 WCF 代码。

实际上,您需要使用CodeDOM 模型编写代码,这需要以非常不同的方式思考代码。不是每个人都能将他们的思想抽象到那个层次。

请记住,利用 CodeDOM 模型,您可以克服 nodots 提到的一些问题,例如数据合约需要可序列化。这可以添加并由基于 CodeDOM 的反射库输出一个新的 DLL。

通过深思熟虑的设计和大量的工作,可以实现您所寻求的输出。这只是一条黑暗的道路,通往那里有很多陷阱。

【讨论】:

【参考方案3】:

是的,这可以通过适当的工具使用适当的工具来完成。如果您拥有 Visual Studio,则您已经拥有 Microsoft 的 T4 代码生成器。它允许您通过编写“文本模板”来生成代码,这很容易让人联想到 ASP.NET 的 RAZOR 语法。使用 T4,您实际上可以实例化您现有的类并使用反射来读取所有类名和方法签名,并最终生成您的 WCF 服务。没那么难!

这是来自Oleg Sych's tutorial 的示例 T4 模板:

<#@ template language=“C#v3.5” #>
<#@ output extension=“SQL” #>
<#@ assembly name=“Microsoft.SqlServer.ConnectionInfo” #>
<#@ assembly name=“Microsoft.SqlServer.Smo” #>
<#@ import namespace=“Microsoft.SqlServer.Management.Smo” #>
<#
    Server server = new Server();
    Database database = new Database(server, “Northwind”);
    Table table = new Table(database, “Products”);
    table.Refresh();
#>
create procedure <#= table.Name #>_Delete
<#
    PushIndent(”\t”);
    foreach (Column column in table.Columns)
    
        if (column.InPrimaryKey)
            WriteLine(”@” + column.Name + ” ” + column.DataType.Name);
    
    PopIndent();
#>
as
    delete from <#= table.Name #>
    where
<#
    PushIndent(”\t\t”);
    foreach (Column column in table.Columns)
    
        if (column.InPrimaryKey)
            WriteLine(column.Name + ” = @” + column.Name);
    
    PopIndent();
#>

输出如下所示:

create procedure Products_Delete
    @ProductID int
as
    delete from Products
    where ProductID = @ProductID

当然,在您的示例中,您将在现有类库上使用反射而不是 sql 查询。您生成的 WCf 服务可以简单地调用您现有的库,因此您不必复制所有实际的域逻辑。

MSDN

https://msdn.microsoft.com/en-us/library/bb126445.aspx

【讨论】:

【参考方案4】:

您可以使用 Roslyn,Roslyn 是新的编译器即服务,您可以在其中解析 C# 文件并根据需要生成源代码。

你可以在这里看到一些例子,

http://www.codeproject.com/Articles/302595/Roslyn-CTP-Three-Introductory-Projects

应该没那么难。

【讨论】:

以上是关于从现有 API 自动生成服务器端 WCF 服务的主要内容,如果未能解决你的问题,请参考以下文章

在现有 WCF SOAP 服务中添加 Http 标头不起作用

将 WCF 服务添加到现有应用程序?

WPF API 可以安全地用于 WCF 服务吗?

从 WCF 服务调用 Web API 端点

如何在现有 WCF 服务中创建新方法?

如何从 WCF-REST API 服务创建 Swagger 规范