DTO 命名约定、建模和继承

问题描述 投票:0回答:2

我们正在使用

AngularJS
C#
ASP.NET Web API
Fluent NHibernate
构建 Web 应用程序。我们决定使用 DTO 将数据传输到表示层(角度视图)。

我对 DTO 的一般结构和命名有一些疑问。这是一个例子来说明我的情况。

假设我有一个名为

Customer
的域实体,它看起来像这样:

public class Customer
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual Address Address { get; set; }
    public virtual ICollection<Account> Accounts { get; set; }
}

现在,在我的视图/表示层中,我需要检索不同风格的

Customer
,例如:

  1. Id
    Name
  2. Id
    Name
    Address
  3. Id
    Name
    Address
    Accounts

我创建了一组 DTO 来完成此任务:

public class CustomerEntry
{
    public  int Id { get; set; }
    public  string Name { get; set; }
}

public class CustomerWithAddress : CustomerEntry
{
    public AddressDetails Address { get; set; }
}

public class CustomerWithAddressAndAccounts : CustomerWithAddress
{
    public ICollection<AccountDetails> Accounts { get; set; }
}

AddressDetails
AccountDetails
是具有其对应域实体的所有属性的 DTO。它们非常适合查询和数据检索。

创建新的

Customer
时,字段
Name
Address
是必填字段,而
Accounts
是可选字段。换句话说,我需要一个具有所有客户属性的对象。因此造成混乱:

  1. 我用什么来插入和更新?这
    CustomerWithAddressAndAccounts
    DTO 拥有一切,但它的名字 对于插入/更新来说似乎有点尴尬。
  2. 我应该创建另一个 DTO 吗?如果我这样做,那岂不是一个 重复,因为新的 DTO 将完全相同
    CustomerWithAddressAndAccounts
  3. 最后但并非最不重要的一点是,DTO继承结构是否描述过 以上似乎很适合要求?还有其他的吗 对此进行建模的方法?

我已经查看了有关此主题的其他帖子,但没有取得太大进展。我学到的一件事是避免在类名称中使用后缀“DTO”。我觉得感觉有点多余。

很想听听您的想法。

谢谢。

c# nhibernate dto
2个回答
25
投票

建议您应该为每个实体添加一个 DTO 类,并以 DTO 为后缀,例如

CustomerEntryDTO
Customer
entity
(但您当然可以根据选择和要求使用继承层次结构)。

此外,添加一个抽象

DTOBase
类型的基类或接口;并且不要对要包含在子 DTO 中的每个地址、帐户和其他属性使用如此深的继承层次结构。相反,请将这些属性包含在同一个
CustomerEntryDTO
类中(如果可能),如下所示:

[Serializable]
public class CustomerEntryDTO : DTOBase, IAddressDetails, IAccountDetails
{
    public  int Id { get; set; }
    public  string Name { get; set; }
    public AddressDetails Address { get; set; } //Can remain null for some Customers
    public ICollection<AccountDetails> Accounts { get; set; } //Can remain null for some Customemer
}

此外,您的 DTO 应该可序列化,以便跨进程边界传递。

有关 DTO 模式的更多,请参阅以下文章:

数据传输对象

MSDN

编辑: 如果您不想通过网络发送某些属性(我知道您需要有条件地发送某些属性,因此需要对此进行更多探索),您可以使用诸如

NonSerialized
之类的属性将它们从序列化机制中排除(但它仅适用于字段,不适用于属性,请参阅与属性一起使用的解决方法文章:属性上的非序列化)。 您还可以创建自己的自定义属性(例如
ExcludeFromSerializationAttribute
),并将其应用于您不想根据某些规则/条件每次都通过网络发送的属性。 另请参阅: 条件 xml 序列化

编辑2: 使用接口来分隔一个

CustomerEntryDTO
类中的不同属性。请参阅 Google 或 MSDN 上的接口隔离原则。稍后我会尝试提供示例解释。


2
投票

我用什么来插入和更新?

  1. 服务操作的定义通常与业务操作非常密切。商业语言不会讲“插入”和“更新”,服务也不会。

  2. 客户管理服务可能有一些

    Register
    操作,需要客户名称以及其他一些可选参数。

我要创建另一个 DTO 吗?

是的,您应该创建另一个 DTO。

有时服务操作契约可能就足够了,不需要为特定操作定义单独的 DTO:

function Register(UserName as String, Address as Maybe(of String)) as Response

但大多数时候最好定义一个单独的 DTO 类,即使只针对单个服务操作:

class RegisterCommand
    public UserName as String
    public Address as Maybe(of String)
end class

function Register(Command as RegisterCommand) as Response

RegisterCommand
DTO 可能看起来与
CustomerWithAddress
DTO 非常相似,因为它们具有相同的字段,但实际上这 2 个 DTO 具有非常不同的含义,并且不能相互替代。

例如,

CustomerWithAddress
包含
AddressDetails
,而简单的
String
地址表示可能足以注册客户。

为每个服务操作使用单独的 DTO 需要更多时间编写,但更易于维护。

© www.soinside.com 2019 - 2024. All rights reserved.