消费/包装大型Web服务的体系结构提示

时间:2012-03-12 13:15:36

标签: c# web-services architecture

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

我一直喜欢用C#开发2年〜,因为我并不总是使用正确的术语,但我会尝试描述我在寻找的东西。

我发布此主题的主要原因是想要了解我应该阅读的内容,设计要实现的模式以及如何以良好的方式管理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. 

XMLParameters:

<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特定类:

创建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模板,其中一个可以根据模板生成类,但我真的没有找到任何好的例子。

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

谢谢你, AMR-它

2 个答案:

答案 0 :(得分:0)

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

public string Create(string sKey, string sXmlParameters) 

输入“sXmlParameters”参数的架构仅由文本文档定义?如果是这种情况,那么我认为你是在过度思考问题,为响应编写解析方法同样有效,因为它将创建类并使用模板和转换技术将响应转换为对象。

但是,你提到XSD.exe,那么输入和输出可能有定义的模式吗?如果是这种情况,我会使用XSD工具创建这些类定义,而不用担心它们不是“整洁的” - 你永远不会看那些c#文件,你只需要使用这些对象。关于它们对集合的弱点 - 如果你试图过滤/排序/检查/遍历复杂的嵌套集合数据,我建议你看一下LINQ查询表达式;他们会让你快速抓住你需要的物品。当然,它们对于对象实例化并没有多大帮助,但我认为没有快速修复它。

答案 1 :(得分:0)

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

我被建议研究DSL流畅的界面,这可以帮助我构建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或similair的类。