【问题标题】:Entity Framework ( Questions on POCO, Context, and DTO)实体框架(关于 POCO、上下文和 DTO 的问题)
【发布时间】:2012-01-09 05:50:43
【问题描述】:

在过去的几天里,我一直在阅读有关实体框架的内容,并设法对使用它有了一个大致的了解,但我仍然有几个问题,其中一些问题可能看起来有点太基本了。从角度来看,我在 asp.net Web 应用程序中使用实体框架 4.0。如果您能回答任何问题,请继续。

  1. 使用 POCO 模板有什么好处。我知道,如果我希望获得持久性无知并让我的实体远离与存储 POCO 实体相关的任何信息,那么我的方法就是走。我也可以在使用 POCO 实体时相对轻松地从实体框架切换到 NHibernate?除了松散耦合之外,我还有任何重要的理由转向 POCO 实体。此外,如果我确实使用 POCO,我最终会失去任何东西。在代理的帮助下,我仍然可以进行更改跟踪和延迟加载吗?

  2. 将 EF 模型的实体用作数据传输对象或业务对象是正常的做法吗?例如,我的实体模型有一个单独的类库。假设我正在使用 MVP ,我想要一个公司中的员工列表。演示者将请求我的业务逻辑函数,该函数将查询实体模型以获取员工列表并将实体列表返回给演示者。在这种情况下,我的演示者需要参考 EF 模型。这是正确的方法吗?就我的 asp.net web applciation 而言,这应该不是问题,但如果我使用的是 web 服务,它是如何工作的?这是去 POCO 实体的原因吗?

  3. 假设 Employee 实体具有指向公司表的导航属性。如果我在“使用”块中使用和包装数据上下文,并尝试访问 BL 中的导航属性,我假设我会遇到异常。如果我关闭延迟加载并使用“包含”linq 查询来获取实体,我是否也会遇到异常?在上一篇文章中,有人建议我为每个请求使用一个上下文,这意味着即使我在 BL 中,该上下文仍保持活动状态。我假设如果我希望保留我所做的任何更改,我仍然需要分离对象并将其附加到我的下一个请求的上下文中?或者我应该使用新的上下文再次查询对象并更新它?

  4. 这个问题更多地与组织文件/最佳实践有关,是我之前发布的问题的后续。当我使用基于实体的单独文件来组织我的数据访问层时,组织涉及多个表之间连接的查询的最佳实践是什么。我对组织仍然有些模糊。曾尝试在线搜索,但没有太多帮助。

【问题讨论】:

    标签: asp.net entity-framework-4 poco


    【解决方案1】:

    很好的问题。我的第一个建议是按模式思考。话虽如此...

    1. 您非常了解使用 POCO 的优势。将业务对象(PO​​CO 实体)与数据访问层分离有一些明显的优势。但主要原因就像您所说的能够更改或修改下面的图层。但是,使用 POCO,您基本上遵循的是代码优先 (CF) 方法。就个人而言,我认为它是并行代码,具体取决于您的软件开发生命周期。您仍然拥有数据或模型优先方法所具有的所有花里胡哨,还有一些因为您可以扩展 DbContext,它是引擎盖下的 ObjectContext。我读了一篇似乎找不到的文章,CF 是实体框架的未来。最后,POCO 的好处是您可以在此处或其他地方合并验证规则。您还可以提供预测。假设您有 Date of Birth,但您也想要一个 Age 属性。现在这变得很简单,因为在映射到数据库时会忽略 Age 属性。
    2. 就我个人而言,我为大型项目创建自己的业务对象 (POCO),这些项目往往有自己的生命,其中改变是一种生活方式。另一个想法是可扩展性和可维护性。如果以后我选择在应用程序之间拆分功能,就像您提到的 Web 服务一样,现在功能是从两个不同的位置提供的。如果您已经将您的业务对象和 DAL 封装在同一个代码块中,那么分离或可伸缩性现在变得有点复杂。但是,请考虑该项目。它可能很小,未来变化很小,因此无需投掷手榴弹杀死苍蝇。在那个时候,数据首先可能是让 edmx 文件代表您的对象的方法。所以不要将自己嫁给一种技术或一种方法/模式。做对您的时间和业务有意义的事情。
    3. 使用语句完全没问题。事实上,我最近开始将其包装在 TransactionScope 中。如果发生错误,回滚是固有的。接下来,要考虑的是 UnitOfWork。 UnitOfWork 模式封装了需要执行的操作的快照,其中数据上下文是您工作的边界。对于每个 UnitOfWork,您都有一个要对其执行工作的主题。例如员工。因此,如果您要保存员工信息以使其保持简单,您将调用 BL 服务或存储库(无论哪种方式)。在那里你传入 Employee Id,在 UnitOfWork 下执行一些工作,它要么在构造函数中实例化,要么使用依赖注入(DI 或 IoC)。简单的入门是 StructureMap。在那里,服务对您的 UnitOfWork (DbContext) 进行必要的调用,然后将控制权返回给上游(例如 UI)。
    4. 在这里学习的最好方法是查看其他代码。我将从一些 Microsoft 示例开始。我将从书呆子晚餐 (http://nerddinner.codeplex.com/) 开始,然后以此为基础。

    补充阅读:

    Use prototype pattern or not

    http://weblogs.asp.net/manavi/archive/2011/05/17/associations-in-ef-4-1-code-first-part-6-many-valued-associations.aspx

    [编辑] NightHawk457,非常抱歉没有回答您的问题。希望您能弄清楚,但对于未来的读者...

    为了帮助大家形象化,想象下面的架构,使用领域模型和存储库作为示例。请记住,有很多方法可以给猫剥皮,所以把它变成你自己的,不要忘记我上面的手榴弹评论。

    • 数据层(数据访问):MyDbContext : DbContext,IUnitOfWork,其中 IUnitWork 合同 CRUD 操作。
    • 数据存储库(数据访问/业务逻辑): MyDomainObjectRepository :IMyDomainObjectRepository,它通过工厂类或依赖注入接收 IUnitOfWork。对 CRUD 操作调用 MyDomainObject 验证。
    • 域模型(业务逻辑):使用 [自定义] 验证属性的 MyDomainObject。阅读this 了解优缺点。
    • MVVM / MVC / WCF(Presentation / Service Layers):无论您选择了哪些附加层,您现在都可以访问您的数据,这些数据很好地包装在较小的模块中封装它们的功能。然后,表示层(例如 ViewModel、Controller、Code-Behind 等)可以通过工厂类或依赖注入接收 IMyObjectRepository。

    提示:

    • 将连接字符串传递给 MyDbContext,以便您可以重用 MyDbContext。
    • MySql 不能很好地与 System.Transactions.TransactionScope 配合使用,example。我不记得确切但这是 MySql 不支持的东西。这使得测试有点困难,因为我们已经创建了这种级别的分离。
    • 为每一层创建一个测试项目,并至少测试一般功能/规则。
    • 每个域对象都应至少使用 ID 字段扩展基础对象。也不要在这里实现 Key 属性。域对象不应该描述架构,而是将特定数据描述为一个实体。即使在 Code First 中,这也可以通过 Fluent API 实现。
    • 在创建 MyDbContext 时考虑泛型。 ;) 阅读 Diego 的 post。
    • 在 ASP.NET 中,存储库非常适合与 ObjectDataSources 一起使用。

    如您所见,角色有明确的分离,其中 IUnitOfWork 和 IMyDomainObjectRepository 是公开上述层功能的接口。例如,IUnitOfWork 可以是 NHibernate、Entity Framework、LinqToSql 或 ADO.NET,其中只需更改工厂类或依赖注入注册即可。仅供参考,我听说存储库也称为服务层。就我个人而言,我喜欢不要将名字与 Web 服务混淆。从此结构中获得的下一个重大成果是实现数据库上下文 (IUnitOfWork) 的范围。一个简单的示例是一个 ASP.NET 页面,其中每个页面对于每个存储库或该工作范围都有一个且只有一个 IUnitOfWork。 ViewModels、Controllers 等也是如此。假设您需要使用两个存储库,EmployeeRepository 和 HRRepository。然后,您可以在两者之间共享或不共享 IUnitOfWork。为了跨页面、ViewModel 或 Controller 边界,我们使用实体的 ID,然后从数据库中提取实体并执行工作。您也可以跨边界传递 DTO 并附加到上下文,但随后您开始失去层的分离。

    要继续,不必自动生成 POCO 类。事实上,您可以从头开始创建实体类,并在 OnModelCreating(DbModelBuilder mb) 方法内的扩展 DbContext 类中执行映射。从 here 开始,然后是 here 并注意 Additional Resources,google Fluent API 并阅读 Diego 的 post。

    至于验证,这是一个有趣的点,因为如果所有业务规则都可以在一个位置进行验证,那就太好了。好吧,我们都知道这并不能很好地工作。所以这是我的建议,在域对象中尽可能保留所有数据级别验证(即必需、范围、格式等)和数据注释,并将流程验证保留在 Repository 中,并具有明确的 Repository 角色(即 if ( isEmployee) 执行此操作,否则执行此操作)。我说得很清楚,这样您就不想在必须重复验证的两个不同存储库中添加员工。要调用验证,请启动 here。捕获 ValidationResults 并使用 MyRepositoryValidationException 向上游发送,其中包含可以呈现给表示层的验证错误集合(例如,需要 Employee)。综上所述,不要忘记在表示层执行验证。例如,您不希望回发以确保员工拥有有效的电子邮件。

    请记住在时间和精力与复杂性之间取得平衡。对于一些简单的事情,对您的 EDMX 文件使用 Data First 或 Model First。然后在上面放置一个存储库,其中还包含所有验证规则。

    【讨论】:

    • 使用 POCO 不是代码优先。您可以先在数据库中使用 POCO,然后再在模型中使用。 Code First 仅指定义模型的方式。在 Code First 中,这是通过流畅的映射完成的,在数据​​库和模型优先中,这是通过 .edmx 文件完成的。
    • 谢谢 Jeff - 我正在研究 UnitOfWork 和 RepositoryPatterns。只有经过一些阅读,才能对 3 做出更好的评论。另外关于向 POCO 类添加验证,我假设您正在谈论通过数据注释进行验证?我假设你必须在这里使用部分类,因为如果 POCO 类是自动生成的,如果它在同一个类中,我们将失去验证。我也必须对此进行一些阅读,但是如果我在这方面偏离了轨道,您将如何进行验证?
    • 感谢您纠正我神秘人。为了扩展您的观点并进一步纠正我自己,我们还可以使用部分类来扩展生成的对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-06
    • 2012-03-08
    • 1970-01-01
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多