【问题标题】:Tips on architecture for consuming/wrapping large webservice使用/包装大型 Web 服务的架构提示
【发布时间】:2012-03-18 01:31:34
【问题描述】:

我正在寻找实现智能架构的良好实践以及处理与具有许多不同 wdsl Web 服务的系统的集成的方法。

我已经有 2 年的时间使用 C# 进行开发了~,因此我并不总是使用正确的术语,但我会尝试描述我在寻找什么。

我发布这篇文章的主要原因是为了了解我应该阅读的领域、要实现的设计模式以及如何以良好的方式管理 API 调用。

我正在与一个提供许多不同 API 的系统进行集成。每个 API 有 20-30 个方法。每个 API 都采用一些 XML 格式的参数。

为了给你一个概念,我以下面的 API 为例,它的 DeviceManager API 和 Create 方法在系统中创建一个设备。 API 接受两个参数,一个字符串键和一个 XML 参数字符串。

示例

DeviceManager

public string Create(string sKey, string sXmlParameters)  

**Parameters:**

Name: sKey Type: string  
Description: 
The API security key. A valid API security key is required for every API call.  

Name: sXmlParameters Type: string  
Description: Values needed to create a new device. See the Additional Information      >section for XML format.  

Return Value: Type: string  
Description: Returns XML containing the status of the creation and if the status is  
Constants.ExternalServiceReturnCodes.SUCCEEDED, the ID of the new device. 

XML参数:

<PARAMETERS>      
    <NAME>string - up to 64 characters. Non-blank name of the device. 
    Must be unique within a gateway.  
    </NAME>   
    <DESCRIPTION>string - up to 1024 characters. The description of the new device.
    </DESCRIPTION> (optional)  
    <TYPEID>string of DeviceType. The type of device. 
    </TYPEID>  
    <GATEWAYID>string - 32-character GUID. The ID of the gateway to associate with the 
    device. If this node is included, it must contain an ID of   
    a gateway that exists in the solution.  
    </GATEWAYID> (optional)  
    <INSTALLATIONDATETIME>  
    date time in UTC - greater than or equal to   
    1753-01-01 00:00:00.000 and less than or equal to   
    9999- 12-31 23:59:59.998. The date time that the device was installed.  
    </INSTALLATIONDATETIME> (optional - if not included, the installation   
                        date time is set to the date time in UTC when the device is 
                        created in the solution)       
    <SERIALNUMBER>string - up to 64 characters. The serial number of the device
    </SERIALNUMBER> 
    <TIMEZONEID>string - time zone ID. The time zone ID of the device.
    Call the TimeZoneManager.RetrieveList API to get a list of valid time zone IDs  
    </TIMEZONEID> (required for device type 'meter')  
    <HARDWAREVERSION>string - up to 32 characters. The hardware version of the device.  
    </HARDWAREVERSION> (optional)  
    <GEOGRAPHICCOORDINATES>  
        <LATITUDE>decimal - greater than or equal to -90 and less than or  
        equal to 90. The latitude geographical coordinate of the   
        device in decimal degrees. The value must be from the   
        WGS84 spatial reference system.                                                                       
        If more than 6 digits after the decimal point are included,   
        the value will be rounded to 6 digits.  
        </LATITUDE>  
        <LONGITUDE>  
        decimal - greater than or equal to -180 and less than or  
        equal to 180. The longitude geographical coordinate of the   
        device in decimal degrees. The value must be from the   
        WGS84 spatial reference system.
        If more than 6 digits after the decimal point are included,   
        the value will be rounded to 6 digits.  
        </LONGITUDE>  
    </GEOGRAPHICCOORDINATES> (optional)
    <METER>  
        <ID>string - 12 hexadecimal characters.</ID> 
        <TRANSFORMERID>string - up to 128 characters.</TRANSFORMERID>  
        <DOWNLIMIT>integer - greater than or equal to 0 and less than or                                                          
        equal to 65535. 
        </DOWNLIMIT> (optional)
    <METER>
</PARAMETERS> 

返回 API 负载格式:

<DEVICEID>string - 32-character GUID. The ID of the new device.</DEVICEID> 

API 总是以以下格式返回数据:

<RETURNS> 
        <STATUS> 
                 String from Constants.ExternalServiceReturnCodes class. 
        </STATUS> 
        <APIPAYLOAD> 
                 Additional information from the API call. Node may be empty. For 
                 APIs that return information, that information will be shown in 
                 the Return section.
        </APIPAYLOAD> 
</RETURNS> 

所以在上面的例子中,有效载荷看起来像:

<RETURNS> 
        <STATUS> 
                 SUCCEEDED
        </STATUS> 
        <APIPAYLOAD> 
                <DEVICEID>string - 32-character GUID. The ID of the new device.</DEVICEID> 
        </APIPAYLOAD> 
</RETURNS> 

目前,我一直致力于为所有 XML 参数设计类,以便能够序列化和反序列化来自 API 的参数和有效负载,但定义这些是一项非常耗时的工作。下面给出了创建 API 参数和有效负载的示例。 (使用get set的简单示例)

DeviceManager 创建参数

[XmlRoot(ElementName = "PARAMETERS")]
public class Create
{

    [XmlElement(ElementName = "NAME")]
    public string Name { get; set; }

    [XmlElement(ElementName = "DESCRIPTION")]
    public string Description { get; set; }

    [XmlElement(ElementName = "TYPEID")]
    public string TypeId { get; set; }

    [XmlElement(ElementName = "GATEWAYID")]
    public string GatewayId { get; set; }

    [XmlElement(ElementName = "INSTALLATIONDATETIME")]
    public string InstallationDateTime { get; set; }

    [XmlElement(ElementName = "SERIALNUMBER")]
    public string SerialNumber { get; set; }

    [XmlElement(ElementName = "TIMEZONEID")]
    public string TimeZoneId { get; set; }

    [XmlElement(ElementName = "HARDWAREVERSION")]
    public string HardWareVersion { get; set; }

    [XmlElement(ElementName = "GEOGRAPHICCOORDINATES")]
    public CreateParametersGeoGraphicCoordinates GeographicCoordinates { get; set; }

    [XmlElement(ElementName = "METER")]
    public CreateMeter Meter { get; set; }
}

public class CreateMeter
{
    [XmlElement(ElementName = "NEURONID")]
    public string NeuronId { get; set; }

    [XmlElement(ElementName = "TRANSFORMERID")]
    public string TransformerId { get; set; }

    [XmlElement(ElementName = "UTILITYID")]
    public string UtilityId { get; set; }

    [XmlElement(ElementName = "LONTALKKEY")]
    public string LonTalkKey { get; set; }

    [XmlElement(ElementName = "DOWNLIMIT")]
    public string DownLimit { get; set; }
}

public class CreateParametersGeoGraphicCoordinates
{
    [XmlElement(ElementName = "LATITUDE")]
    public string Latitude { get; set; }

    [XmlElement(ElementName = "LONGITUDE")]
    public string Longitude { get; set; }
}

对于 PayLoads,我有以下通用类和 DeviceManager.Create Payload 特定类:

创建有效负载

public class CreatePayLoad
{
    [XmlElement(ElementName = "DEVICEID")]
    public string DeviceId { get; set; }
}

有效负载

[XmlRoot(ElementName = "RETURNS")]
public class PayLoad<T> where T : new()
{
    public PayLoad()
    {
        ApiPayLoad = new T();
    }

    /// <summary>
    /// Contains the payload from the command.
    /// </summary>
    [XmlElement(ElementName = "APIPAYLOAD")]
    public T ApiPayLoad { get; set; }

    /// <summary>
    /// Status of the call
    /// </summary>
    [XmlElement(ElementName = "STATUS")]
    public string Status { get; set; }
}

所以在我的代码中,我可以通过以下方式进行调用:

使用示例

//Create the parameters
var parameters = new Create
                     {
                         Description = "Description",
                         GatewayId = "GatewayId",
                         Name = "NameOfDevice",
                         GeographicCoordinates = new CreateParametersGeoGraphicCoordinates
                                                     {
                                                         Latitude = "Lat",
                                                         Longitude = "Long"
                                                     },
                         Meter = new CreateMeter
                                     {
                                         TransformerId = "ID",
                                         DownLimit = "120"
                                     }
                     };

//Serialize the parameters to xml
string sXmlParameters = Helper.SerializeToXml<Create>(parameters);

//API call
string apiPayLoad = DeviceManager.Create(apiKey, sXmlParameters);

//Deserialize payload
var payLoad = Helper.DeserializeFromXml<PayLoad<CreatePayLoad>>(apiPayLoad);

有人可以提供想法和更好的方法来管理它吗? 请记住,系统中大约有 300 种方法,其中一些具有非常复杂的 xml 参数,其中一些属性是可选的,如果使用属性 A、B 或 C,则一些是必需的等等。

我一直在查看 XSD.exe,但生成的代码看起来并不整洁,并且不能很好地处理集合。

一位朋友也提出了 T4 模板,可以根据模板生成类,但我并没有真正找到任何好的示例。

我不确定我是否以一种好的方式解释了自己,如果我不清楚 - 请告诉我,我会尽力详细说明。

谢谢, 好啦

【问题讨论】:

    标签: c# web-services architecture


    【解决方案1】:

    愚蠢的提问时间:所以你是说没有一个 API 是强类型的?意思是,所有的签名都是:

    public string Create(string sKey, string sXmlParameters) 
    

    输入“sXmlParameters”参数的模式仅由文本文档定义?如果是这种情况,那么我认为您想多了,编写响应的解析方法与创建类并使用模板和转换技术将响应转换为对象一样有效。

    但是,您提到了 XSD.exe,所以也许有已定义的输入和输出模式?如果是这种情况,我会使用 XSD 工具创建这些类定义,而不必担心它们不“顺眼”——你从不查看那些 c# 文件,你只是使用对象。就它们对集合的弱点而言 - 如果您尝试过滤/排序/检查/遍历复杂的嵌套集合数据,我建议您查看 LINQ 查询表达式;它们会让您快速抓取所需的物品。当然,它们对对象实例化没有多大帮助,但我认为没有快速解决方法。

    【讨论】:

      【解决方案2】:

      它们不是强类型的,所有 API 都采用 sXmlParameters 并且仅由文本文档定义。使用 XSD.exe,我制作了 XML 示例,然后生成了 xsd,并从中生成了一些 .cs 文件。但是,这是一项非常耗时的任务。

      建议我研究 DSL fluent 接口,这可以帮助我构建 sXmlParameters。

      var result = DeviceManager.Create(apiKey,
        Parameters.
        .Type(Parameters.Create)
        .Name("Name of Device")
        .Description("Description of Device")
        .Coordinates("Lat","Long")
        .Validate()
        .asXML()
      );
      

      使用它,我可以从代码中构造参数,并有一个基类在将参数作为 XML 返回之前验证所需的字段。

      但是,为每个结果映射 PayLoads 仍然是一项耗时的任务。我正在考虑使用 T4 模板来生成基于 XML 或类似的类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-11
        • 2012-10-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多