46

我们正在使用 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; }
    }

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

1) 只是身份证和姓名 2) 身份证、姓名和地址 3) 身份证、姓名、地址和帐户

我创建了一组 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。

这适用于查询和数据检索;问题是我用什么来插入和更新。在创建新客户记录期间,姓名和地址是必需的,帐户是可选的..所以换句话说,我需要一个包含所有客户属性的对象。因此混乱:

1) 我用什么来插入和更新?CustomerWithAddressAndAccounts DTO 包含所有内容,但它的名称似乎有点难以用于插入/更新。

2)我是否创建另一个 DTO .. 如果我这样做,那不是重复,因为新的 DTO 将与 CustomerWithAddressAndAccounts 完全一样?

3)最后但并非最不重要的一点是,上述 DTO 继承结构似乎很适合要求吗?还有其他方法可以对此建模吗?

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

很想听听你的想法

谢谢

4

3 回答 3

21

建议您应该为每个后缀为 DTOCustomerEntryDTO的实体设置一个 DTO 类,例如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(但是它仅适用于字段而不适用于属性,请参阅使用属性的解决方法文章:NonSerialized on property)。您还可以创建自己的自定义属性,例如,ExcludeFromSerializationAttribute根据某些规则/条件,将其应用于您不想每次通过线路发送的属性。另请参阅: 条件 xml 序列化

编辑 2: 使用接口来分隔一个CustomerEntryDTO类中的不同属性。请参阅 Google 或 MSDN 上的接口隔离原则。稍后我将尝试进行示例说明。

于 2013-09-16T18:26:07.180 回答
0

我用什么来插入和更新?

  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

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

例如,CustomerWithAddresscontains AddressDetails,而简单的String地址表示可能足以注册客户。

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

于 2016-01-04T13:42:12.313 回答
-1

从您的第 1 项开始,对于插入和更新,最好使用命令模式。根据 CQRS,您不需要 DTO。考虑这个架构: CQRS — 基本模式 通过blogs.msdn.com

于 2014-09-10T19:22:05.980 回答