1

我已经做了很多搜索和试验,但一直无法找到解决这个问题的可行方法。

环境/工具

  • 视觉工作室 2013
  • C#
  • 三层网络应用程序:
    • 数据库层: SQL Server 2012
    • 中间层: Entity Framework 6.* 使用 Database First,Web API 2.*
    • 表示层: MVC 5 w/Razor、Bootstrap、jQuery 等。

背景

我正在为需要严格的三层架构的客户端构建一个 Web 应用程序。具体来说,表示层必须通过 Web 服务执行所有数据访问。表示层不能直接访问数据库。该应用程序允许一小部分付费员工管理人员、等候名单和他们正在等待的资源。根据需求,数据模型/数据库设计完全以人(用户表)为中心。

问题

当表示层请求某些东西时,比如资源,它至少与一个用户相关,而用户又与其他一些表相关,比如角色,这些表与更多用户相关,这些用户与更多角色和其他相关事物。关键是,当我查询 EF 想要引入的几乎整个数据库的任何内容时。

由于 EF 的默认延迟加载行为,这通常是可以的,但是当将任何对象序列化为 JSON 以返回到表示层时,Newtonsoft.Json 序列化程序会挂起很长时间,然后引发堆栈错误。

我试过的

这是我到目前为止所尝试的:

  1. 将 Newtonsoft 的 JSON 串行器 ReferenceLoopHandling 设置设置为 Ignore。没运气。这不是循环图问题,它只是引入的大量数据(有超过 20,000 个用户)。

  2. 清除/重置不需要的集合并将引用属性设置为 null。这显示了一些希望,但我无法绕过 Entity Framework 跟踪所有内容的愿望。

    • 只需将导航属性设置为 null/clear 会导致这些更改在下一个 .SaveChanges() 上保存回数据库(注意:这是一个假设,但看起来很合理。如果有人知道不同,请说出来)。

    • 分离实体会导致 EF 自动清除所有集合并将所有引用属性设置为 null,无论我是否想要它。

    • 对所有内容使用 .AsNotTracking() 会引发一些关于不允许非跟踪实体具有导航属性的异常(我不记得确切的细节)。

  3. 使用 AutoMapper 制作对象图的副本,仅包括我指定的相关对象。这种方法基本上是有效的,但在(我相信)执行自动映射的过程中,所有导航属性都被访问,导致 EF 查询和解决它们。在一种情况下,这会在对 Web 服务的单个请求期间导致近 300,000 次数据库调用。

我在寻找什么

简而言之,之前有没有人必须解决这个问题并提出一个有效的解决方案?

缺乏这一点,任何关于至少在哪里寻找如何处理这个问题的指针都将不胜感激。

附加说明:当我写这篇文章时,我想到我可以将上面的第二项和第三项结合起来。换句话说,设置/清除导航属性,然后将图形自动映射到新对象,然后分离所有内容以使其不会被保存(或者可能将其包装在事务中并在最后回滚)。但是,如果有更优雅的解决方案,我宁愿使用它。

谢谢,戴夫

4

4 回答 4

1

确实,按照您的要求去做是非常困难的,而且这是一个架构陷阱,我看到很多项目都陷入了困境。

即使这个问题是可以解决的,你基本上最终只会有一个数据层,它只是包装数据库并破坏性能,因为你不能正确地利用 SQL。

相反,请考虑以返回包含有意义数据的有意义对象的方式构建您的数据访问服务;也就是说,只有执行需求文档中概述的特定任务所需的数据。确实,帖子与帐户相关,帐户有很多成就等等。但通常我想要的只是发帖者的文字和名称。而且我不想在一篇文章中使用它。我希望它用于页面中的每个帖子。相反,编写与您的应用程序相关的数据服务和方法。

需要澄清的是,返回包含仅包含发布者名称和消息的帖子列表的 Page 对象与返回包含大量不相关数据(例如 ID)、审核数据(例如创建时间)的整个 EF 对象之间的区别。

考虑 Twitter API。如果按照上面的方式实施,那么 Twitter 获得的流量将非常糟糕。并且返回的大部分信息(消耗 CPU 时间、磁盘活动、数据库连接打开时间更长、网络带宽)与开发人员想要做什么完全无关。

相反,该 API 公开了对希望制作 Twitter 应用程序的开发人员有用的内容。给我这个用​​户的帖子。给我这个用​​户的简历。对于像 Twitter 这样大的人来说,这可能实现为非常好的调整 SQL 查询,但对于较小的客户端,只要您不试图破坏它的性能特性,EF 就很棒。

这也使测试更容易,因为更小、更相关的数据对象更容易模拟。

于 2014-07-05T20:21:17.363 回答
0

对于三层应用程序,特别是如果您要在服务中“原始”公开实体,我建议您在 EF 中禁用延迟加载和代理生成。您的替代方法是使用 DTO 而不是实体,以便 Web 服务返回为服务而不是实体量身定制的模型对象(如 jameswilddev 建议的那样)

无论哪种方式都行得通,并且有各种取舍。

如果您在多层环境中使用 EF,我强烈推荐 Julia Lerman 的 DbContext 书(我没有隶属关系):http ://www.amazon.com/Programming-Entity-Framework-Julia-Lerman-ebook/dp /B007ECU7IC

书中有一章专门介绍在多层环境中使用 DbContext(您将看到关于延迟加载和代理的相同建议)。它还讨论了如何在多层环境中管理插入和更新。

于 2014-07-05T21:30:18.767 回答
0

我有这样一个压力很大的项目....而且我需要加载大量数据并从不同角度处理它们并将其传递给复杂的仪表板以获取图表和表格。我的优化是:

1-而不是使用 ef 加载数据,我称为老式存储过程(以及更多优化分组内容以尽可能减少图表的表格。例如,查询返回一个可以从中提取多个图表数据集的表格)

2-更重要的是,我使用了 FastJSON 而不是 Newtonsoft 的 JSON,它的性能值得一提(它真的很快,但与复杂的对象不兼容。简单的例子可能是视图模型,里面有模型列表,等等等等,或者)更好之前阅读 fastJSON 的优缺点

https://www.codeproject.com/Articles/159450/fastJSON

3-in 关系数据库设计谁是这个问题的主要嫌疑人,创建那些具有原始数据以在(很可能用于分析)非规范化模式中处理的表可能会很好,这可以节省查询数据的性能。还要注意使用数据库中的 EF 设计器中的模型类来读取或选择数据,尤其是当你想要序列化它时(有时我认为将相同的模式模型分成两个相同的类/模型的部分,以便以这样的方式写入和读取数据写入模型具有来自外键的虚拟集合的好处,而读取模型忽略它......我不确定)。

注意:如果数据非常庞大,最好更深入地为包含事实或原始数据的某个表设置内存表 OLTP,但在这种情况下,您的表就像 noSQL 那样充当非关系表。

注意:例如在 mssql 中,您可以使用 sqlCLR 的好处,它可以让您在 c#、vb..etc 中编写脚本并通过 t-sql 调用它们,换句话说,从数据库级别处理数据处理。

4-对于需要加载数据的交互式视图,我认为最好考虑哪些信息可能在服务器端处理,哪些信息可以由客户端处理(有时最好从客户端查询数据......应该考虑到客户端的那些数据可以被用户访问)不管它是如何根据情况的。

5-在视图中使用大型原始数据表的情况下datatables.min.js是一个好主意,而且每个人都建议在表上使用服务器端分页。

6-如果从大文件导入和导出数据oledb是我认为的最佳选择。

我仍然怀疑它们是否是确切的解决方案。如果任何机构有实际的解决方案,请提及;)。

于 2020-04-29T17:38:36.450 回答
-1

我首先使用 EF 模型解决了类似的问题,并发现以下解决方案满足“一对多”关系:

  • 在子实体中包含“外键属性”并将其用于以后的查找。
  • 将 EF 实体中任何“导航属性”(子集合)的 get/set 修饰符定义为私有。

数据关系示例(摘录)

这将为您提供一个不公开子集合的对象,并且您只会获得序列化的主要属性。此解决方法将需要对您的 LINQ 查询进行一些重组,直接从您的 SubItems 表中询问外键属性作为您的过滤选项,如下所示:

var myFitnessClubs = context.FitnessClubs
    ?.Where(f => f.FitnessClubChainID == myFitnessClubChain.ID);

注意 1:您可能会选择部分实现此解决方案,因此只会影响您强烈不想序列化的子集合。

注 2:对于“多对多”关系,至少有一个实体需要公开表示该集合。由于无法使用单个 ID 属性检索关系。

于 2017-08-23T14:36:36.883 回答