C# DTO命名约定、建模和继承
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18834565/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
DTO naming conventions , modeling and inheritance
提问by Sennin
We are building a web app using AngularJS , C# , ASP.Net Web API and Fluent NHibernate. We have decided to use DTOs to transfer data to the presentation layer ( angular views). I had a few doubts regarding the general structuring and naming of DTOs. Here's an example to illustrate my scenario. Lets say I have a domain entity called Customer which looks like:
我们正在使用 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; }
}
Now, in my views/presentation layer I need to retrieve different flavors of Customer like :
现在,在我的视图/表示层中,我需要检索不同风格的 Customer,例如:
1) Just Id and Name 2) Id , Name and Address 3) Id , Name , Address and Accounts
1) 只有 Id 和 Name 2) Id , Name 和 Address 3) Id , Name , Address 和 Accounts
I have created a set of DTOs to accomplish this :
我创建了一组 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 and AccountDetails are DTOs which have all the properties of their corresponding Domain entities.
AddressDetails 和 AccountDetails 是 DTO,它们具有其相应域实体的所有属性。
This works fine for querying and data retrievals ; the question is what do I use for inserts and updates. During creation of a new customer record , name and address are mandatory and accounts are optional ..so in other words I need an object with all the customer properties. Hence the confusion :
这适用于查询和数据检索;问题是我用什么来插入和更新。在创建新客户记录期间,姓名和地址是必需的,帐户是可选的..所以换句话说,我需要一个具有所有客户属性的对象。因此混乱:
1) What do I use for insert and updates? The CustomerWithAddressAndAccounts DTO has everything in it but its name seems a bit awkward to be used for insert/updates.
1) 我用什么来插入和更新?CustomerWithAddressAndAccounts DTO 包含所有内容,但它的名称用于插入/更新似乎有点尴尬。
2) Do I create another DTO .. if I do , wouldn't that be duplication as the new DTO will exactly be like CustomerWithAddressAndAccounts ?
2)我是否创建另一个 DTO .. 如果我这样做,那不是重复,因为新的 DTO 将完全像 CustomerWithAddressAndAccounts 吗?
3) Last but not least , does the DTO inheritance strcuture described above seem like a good fit for the requirement ? Are there any other ways to model this ?
3) 最后但并非最不重要的一点是,上面描述的 DTO 继承结构似乎很适合需求?有没有其他方法可以对此进行建模?
I have gone through other posts on this topic but couldn't make much headway. One thing that I did pickup was to avoid using the suffix "DTO" in the class names. I think it feels a bit superfluous.
我已经阅读了有关此主题的其他帖子,但没有取得太大进展。我做的一件事是避免在类名中使用后缀“DTO”。我觉得感觉有点多余。
Would love to hear your thoughts
很想听听你的想法
Thanks
谢谢
回答by VS1
Recommendation is that you should just have one DTO class for each entity suffixed with DTOe.g. CustomerEntryDTO
for the Customer
entity
(but you can certainly use inheritance hierarchies as per choice and requirements).
建议您应该为每个后缀为 DTO 的实体设置一个 DTO 类,例如CustomerEntryDTO
for Customer
entity
(但您当然可以根据选择和要求使用继承层次结构)。
Moreover, Add a abstract DTOBase
kind of base class or an interface; and do not use such deep inheritance heirarchies for each Address, Account and other properties to be included in child DTOs. Rather, include these properties in the same CustomerEntryDTO
class (if possible) as below:
此外,添加一个抽象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
}
Moreover, your DTOs shouldbe serializable to be passed across process boundaries.
此外,您的 DTO应该是可序列化的,以便跨进程边界传递。
For moreon the DTO pattern, refer below articles:
有关DTO 模式的更多信息,请参阅以下文章:
Edit:In case you don't want to send certain properties over the wire (I know you would need to that conditionally so would need to explore more on this), you can exclude them from the Serialization mechanism by using attributes such as NonSerialized
(but it works only on fields and not properties, see workaround article for using with properties: NonSerialized on property).
You can also create your own custom attribute such as ExcludeFromSerializationAttribute
and apply it to properties you don't want to send every time over wire based on certain rules/conditions. Also see:Conditional xml serialization
编辑:如果您不想通过网络发送某些属性(我知道您需要有条件地这样做,因此需要对此进行更多探索),您可以使用诸如NonSerialized
(但是它仅适用于字段而不适用于属性,请参阅与属性一起使用的解决方法文章:属性上的非序列化)。您还可以创建自己的自定义属性,例如,ExcludeFromSerializationAttribute
根据某些规则/条件,将其应用于您不想每次都通过网络发送的属性。另请参阅:条件 xml 序列化
Edit 2:Use interfaces for separating the different properties in the one CustomerEntryDTO
class. See the Interface Segregation Principle on Google or MSDN. I will try to put a sample explanation later.
编辑 2:使用接口来分离一个CustomerEntryDTO
类中的不同属性。请参阅 Google 或 MSDN 上的接口隔离原则。稍后我会尝试放一个示例说明。
回答by Vitaly Chirkov
As of your item 1, for inserts and updates it's better to use Command pattern. According to CQRS, you don't need DTOs. Consider this schema:
via blogs.msdn.com
从您的第 1 项开始,对于插入和更新,最好使用命令模式。根据 CQRS,您不需要 DTO。考虑这个模式:
通过blogs.msdn.com
回答by Lightman
What do I use for insert and updates?
我用什么来插入和更新?
Service operations are usually defined in very close relation to business operations. Business language doesn't speak in terms of "inserts" and "updates", neither do services.
Customer management service is likely to have some
Register
operation that takes customer name and maybe some other optional parameters.
服务操作通常与业务操作密切相关。商业语言不涉及“插入”和“更新”,服务也不涉及。
客户管理服务可能有一些
Register
操作需要客户名称和其他一些可选参数。
Do I create another DTO?
我要创建另一个 DTO 吗?
Yes, you should create another DTO.
是的,您应该创建另一个 DTO。
Sometimes service operation contract may be enough and there is no need to define a separate DTO for a particular operation:
有时服务操作契约可能就足够了,不需要为特定操作定义单独的 DTO:
function Register(UserName as String, Address as Maybe(of String)) as Response
But most of the time it is better to define a separate DTO class even for only a single service operation:
但大多数情况下,即使仅针对单个服务操作,最好定义一个单独的 DTO 类:
class RegisterCommand
public UserName as String
public Address as Maybe(of String)
end class
function Register(Command as RegisterCommand) as Response
RegisterCommand
DTO may look very similar to CustomerWithAddress
DTO because it has the same fields but in fact these 2 DTOs have very different meanings and do not substitute each other.
RegisterCommand
DTO 可能看起来与CustomerWithAddress
DTO非常相似,因为它具有相同的字段,但实际上这 2 个 DTO 具有非常不同的含义并且不能相互替代。
For example, CustomerWithAddress
contains AddressDetails
, while a simple String
address representation may be enough to register a customer.
例如,CustomerWithAddress
contains AddressDetails
,而简单的String
地址表示可能足以注册客户。
Using a separate DTO for each service operation takes more time to write but easier to maintain.
为每个服务操作使用单独的 DTO 需要更多时间来编写但更易于维护。