WCF数据合同和参考实体数据?

时间:2009-06-16 23:11:22

标签: wcf design-patterns soa datacontract

征求关于“最佳”模式的反馈/选项/评论,以用于我的服务中的参考数据。

参考数据是什么意思?

我们以Northwind为例。订单与数据库中的客户相关。当我实现我的订单服务时,在某些情况下,当我只想要一个对客户的引用(例如一个键/值对)时,我会希望引用来自订单的“完整”客户和其他情况。

例如,如果我正在使用GetAllOrders(),我不想返回完全填写的订单,我想要返回订单的轻量级版本,其中只包含每个订单的Customer的参考数据。但是,如果我使用GetOrder()方法,我可能想填写客户详细信息,因为很可能需要此方法的消费者。可能还有其他情况我可能要求在某些方法调用期间填写客户详细信息,但其他情况则遗漏。

以下是我的想法:

[DataContract]
public OrderDTO
{
    [DataMember(Required)]
    public CustomerDTO;

    //etc..
}

[DataContract]
public CustomerDTO
{
    [DataMember(Required)]
    public ReferenceInfo ReferenceInfo;

    [DataMember(Optional)]
    public CustomerInfo CustomerInfo;
}

[DataContract]
public ReferenceInfo
{
    [DataMember(Required)]
    public string Key;

    [DataMember(Required)]
    public string Value;
}

[DataContract]
public CustomerInfo 
{
    [DataMember(Required)]
    public string CustomerID;

    [DataMember(Required)]
    public string Name;

    //etc....
}

这里的想法是,因为在CustomerDTO中总是需要ReferenceInfo(它是一个通用的键/值对),所以我总是有ReferenceInfo。如果需要,它可以为我提供足够的信息以获取客户详细信息。让CustomerDTO需要ReferenceInfo的缺点是,当我获得完整的CustomerDTO(即填写了CustomerInfo)时,它可能有点过分,但至少我保证参考信息。

我可以使用其他一些模式或框架来使这个场景/实现“更清洁”吗?

我问的原因是,尽管我们可以简单地说在Northwind中总是返回一个完整的CustomerDTO,但这在单纯的Northwind情况下可能会正常工作。在我的例子中,我有一个对象,有25-50个字段是引用/查找类型数据。有些在不同情况下加载比其他更重要,但我希望尽可能少地定义这些引用类型(这样我就不会进入“DTO维护地狱”)。

评论?反馈?评论

谢谢!

7 个答案:

答案 0 :(得分:2)

我们的项目处于同一决策点。截至目前,我们已决定创建三个级别的DTO来处理Thing:SimpleThing,ComplexThing和FullThing。我们不知道它对我们有什么影响,所以这还不是基于现实的答案。

我想知道的一件事是,我们是否可以了解到我们的服务是在“错误”级别设计的。例如,是否有一个实例我们应该将FullThing分开并仅通过SimpleThing?如果我们这样做,这是否意味着我们不恰当地将某些业务逻辑置于过高的水平?

答案 1 :(得分:2)

亚马逊产品广告API网络服务是您遇到的同一问题的一个很好的例子。

他们根据具体情况使用不同的DTO为呼叫者提供更多或更少的细节。例如,有small response grouplarge response group和中间medium response group

如果你说你不想要一个讨人喜欢的界面,拥有不同的DTO是一种很好的技巧。

答案 2 :(得分:1)

这对我来说似乎是一个复杂的解决方案。为什么不在OrderDTO类中只有一个customer id字段,然后让应用程序在运行时决定它是否需要客户数据。由于它具有客户ID,因此可以在决定时将数据拉下来。

答案 3 :(得分:1)

我决定反对我要采取的方法。我认为我最初的担忧主要是缺乏要求。我有点期待这种情况,但很想知道其他人如何处理这个确定何时加载某些数据以及何时不加载的问题。

我正在展平我的数据合同以包含最常用的参考数据元素字段。这应该适用于大多数消费者。如果提供的数据对于给定的消费者来说不够,他们可以选择查询单独的服务以撤回特定参考实体的完整详细信息(例如货币,州等)。对于实际上基本上是键/值对的简单查找,我们将使用通用键/值对数据契约来处理它们。我甚至可以将KnownType属性用于更专业的键/值对。

[DataContract]
public OrderDTO
{
    [DataMember(Required)]
    public CustomerDTO Customer;

    //in this case, I think consumers will need currency data, 
    //so I pass back a full currency item
    [DataMember(Required)]
    public Currency Currency; 

    //in this case, I think consumers are not likely to need full StateRegion data, 
    //so I pass back a "reference" to it
    //User's can call a separate service method to get full details if needed, or 
    [DataMember(Required)]
    public KeyValuePair ShipToStateRegion;

    //etc..
}


[DataContract]
[KnownType(Currency)]
public KeyValuePair
{
    [DataMember(Required)]
    public string Key;

    [DataMember(Required)]
    public string Value;

    //enum consisting of all possible reference types, 
    //such as "Currency", "StateRegion", "Country", etc.
    [DataMember(Required)]
    public ReferenceType ReferenceType; 
}


[DataContract]
public Currency : KeyValuePair
{
    [DataMember(Required)]
    public decimal ExchangeRate;

    [DataMember(Required)]
    public DateTime ExchangeRateAsOfDate;
}


[DataContract]
public CustomerDTO 
{
    [DataMember(Required)]
    public string CustomerID;

    [DataMember(Required)]
    public string Name;

    //etc....
}

思考?意见?评论

答案 4 :(得分:1)

我们也在对象关系映射中遇到了这个问题。在某些情况下,我们需要完整的对象以及我们想要引用它的其他对象。

难点在于,通过将序列化本身烘焙到类中,数据提取模式强制认为只有一种正确的方式来序列化对象。但是在很多情况下,您可能希望部分地序列化类和/或其子对象。

这通常意味着每个班级必须有多个DTO。例如,FullCustomerDTO和CustomerReferenceDTO。然后,您必须创建将不同DTO映射回Customer域对象的方法。

你可以想象,这是一项繁重的工作,其中大部分工作非常繁琐。

答案 5 :(得分:1)

另一种可能性是将物品视为物品袋。在查询时指定所需的属性,并准确返回所需的属性。

更改属性以显示在“短”版本中然后不需要多次往返,您可以一次获取一组的所有属性(避免繁琐的界面),并且您不必修改如果您确定“短”版本需要不同的属性,那么您的数据或操作合同。

答案 6 :(得分:1)

我通常构建延迟加载到我的复杂Web服务(即发送/接收实体的Web服务)。如果一个Person有一个父属性(也是一个Person),我只发送一个父的标识符而不是嵌套对象,然后我只是确保我的web服务有一个可以接受一个标识符的操作并用相应的Person实体作出响应。然后,如果客户端想要使用父属性,则可以回调用该Web服务。

我还对此进行了扩展,以便可以进行批处理。如果一个操作发回5个人,那么如果在其中任何一个人上访问了父属性,则会向所有5个父亲发出一个请求,并提供他们的标识符。这有助于减少Web服务的干扰。