2013-09-16 189 views
26

我们正在使用AngularJS,C#,ASP.Net Web API和Fluent NHibernate构建一个Web应用程序。 我们决定使用DTO将数据传输到表示层(角度视图)。 我对DTO的一般结构和命名有一些疑问。 下面是一个例子来说明我的情况。 可以说,我有一个域的实体,称为客户看起来像:现在DTO命名约定,建模和继承

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)ID,姓名,地址和帐户

我创建了一个集的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继承strcuture看起来是否符合要求?有没有其他方法可以对此进行建模?

我已经通过了这个话题的其他文章,但无法取得进展。 我做的一件事是避免在类名中使用后缀“DTO”。 我觉得这有点多余。

很想听听您的想法

感谢

回答

9

建议是,你应该只需要一个DTO类为每个实体与DTO例如后缀CustomerEntryDTOCustomerentity(但您可以根据选择和要求当然使用继承层次结构)。

此外,添加一个摘要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模式,是指下面的文章:

Data Transfer Object

MSDN

编辑: 如果你不想通过网络发送的某些属性(我知道你需要这样的条件,所以需要在这方面进行更多的探索),你可以通过使用诸如之类的属性从序列化机制中排除它们(但它仅适用于字段而不适用于属性,请参阅适用于属性的解决方法文章:NonSerialized on property)。 您也可以创建自己的自定义属性,例如ExcludeFromSerializationAttribute,并将其应用于您不希望每次通过有线规则/条件发送的属性。 另见:Conditional xml serialization

编辑2: 在一个CustomerEntryDTO类分开不同的属性使用的接口。请参阅Google或MSDN上的界面分离原则。我会尽量在稍后提供样本解释。

+0

感谢您的回复。使用多个DTO类的动机之一是在定义DTO的用途时更具表现力。另外,如果您只需要Id和Name,您是否应该通过电汇发送完整的CustomerEntryDTO(您所描述的)? – Sennin

+0

@Sennin我根据你的评论编辑了我的答案,但我认为我的答案对你而言仍然不完整。可能是我稍后将对其进行编辑,以在您的上述评论中添加更多内容。 – VS1

+0

@Sennin请参阅我的编辑2. – VS1

0

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

+0

你仍然需要一个DTO用于查询事物,所以这不是一个好的答案。 –

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 

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

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

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