我有一个有三层的应用程序: 1.数据层:公开实体框架实体 2.业务逻辑层:查询EF模型,获取实体并暴露DTO(数据传输对象) 3. UI层:查询BL,获取DTO并查看它们。
现在我有一个问题。我的应用程序的不同部分需要相同的DTO,但有一些不同的字段。为简单起见,假设我的一个BL类暴露了名为Person的DTO,其中Name和Surname需要一次,并且在其他地方显示名称和出生日期。
我想听听您对我的简单解决方案的看法。我已经来了,我的UI必须在“DTO合同”上同意BL,以便两层在课堂上达成一致。在我的例子中,我会:
a)创建一个抽象的Person类。这个类没有任何方法也没有字段 b)在BL中创建一个名为GetPerson的方法,该方法接受Person类作为参数 c)定义两个或多个派生自Person的类(假设PersonName和PersonDOB) d)我的UI调用GetPerson传递所需类型(如GetPerson(typeof(PersonName))) e)BL填写Person类
你怎么看?有没有更好的解决方案?我认为这不是那么好买不到我想的更好。
非常感谢。 马可
答案 0 :(得分:4)
我的MVC2应用程序中有类似的架构。我做了什么:
BL返回具有Name,Surname和DoB的Person DTO(所有这些属性都是Person的一部分)
我的视图使用模型,因此我为每个视图创建特定模型。因此我会有2个人模型,即。一个是Name和Surname,另一个是Name和DoB。
DTO和模型之间的转换是由一组我称之为Adapters的类完成的。为了简化代码,我使用 Automapper 。这是一段精彩的代码,它将通过考虑命名约定和显式配置将属性从DTO复制到模型。请看一下,因为您可能想用它来填充EF类中的DTO。
总结一下,我有一个没有任何气味的一致BL(这个'给我一个这种类型的子类'业务对我来说有点嗅觉),我的观点正在使用只包含相关数据的强类型模型
答案 1 :(得分:2)
有两件事:首先是术语。我不是要成为一个术语纳粹 - 只是存在一些微妙的差异,可能会影响你对你的问题的看法。
DTO与POCO
当谈到DTO(数据传输对象)时,它听起来更像是POCO(普通旧CLR对象)。
波苏斯:
在此背景下(特定POCO的设计)SRP将意味着“业务”驱动的使用案例(即:显示搜索结果中“人”数据的摘要)或通用/数据中心(即:提供'人')。
DTO:
直到最近我才盲目地认为POCO == DTO;当然很多人(我认为)倾向于以这种方式谈论它们;然后我读了Martin Fowler对DTO的定义,这是不同的。
因此,可以想象您可以使用DTO将多个POCO传递给外部服务。
您的问题的答案
设计POCO(以及层和组件之间的接口)时,最重要的应该是“为什么”?在设计POCO(和DTO)时,您会考虑一些不同的观点和动机:
所以,您可以考虑以下两种方法......
使用混凝土POCO驱动的责任
设计并构建一个POCO(如在实际的类或结构中),完成你想要的工作;这将沿着上面讨论的路线(“业务”或“通用/以数据为中心”)。
这就是我目前的做法。我经常有一个'胖'POCO,它定义了一个实体(有时包括其他POCO),以及一个用于列表的'瘦'POCO。我也倾向于使用单独的POCO进行保存和更新。
虽然标准化为什么POCO的设计方式是有意义的(例如:它们都是由域模型驱动的,每个实体只有一个POCO) - 事实是你会有不同的动机来自不同的方向;我的建议是屈服于此,否则你最终会得到一个不灵活,易于维护或高效的系统。
使用界面
这是一种更“纯粹”的方法。而不是让您的应用程序通过使用接口来传递具体的POCO。然后,当您构建实现POCO时,它可以根据您的需要实现尽可能多的接口。例如:
public interface PersonID
{
Guid PersonID { get; }
}
public interface PersonFullName
{
string FirstName { get; }
string Lastname { get; }
string Honorific { get; }
}
public interface PersonDateOfBirth
{
DateTime DateOfBirth { get; }
}
奖励积分
你没有要求这个(至少没有直接),但我不会将我的BL绑定到EF - 实际上我根本不会将它绑定到数据访问层。如果你这样做,任何想要使用BL的东西都将与EF绑在一起。您可能需要考虑Dependency Inversion。