2

我的模型设计中有一个非常深的关系树,即根实体包含一个实体集合,该集合包含更多其他实体的集合,该集合包含更多集合并且在...我开发了一个其他开发人员拥有的业务层用于执行操作,包括获取/保存数据。

然后,我正在考虑应对这种情况的最佳策略是什么。我不能允许在检索实体时,EF 解析所有依赖关系树,因为它将以很多无用的 JOIN 结束(没用,因为我可能不需要下一级的数据)。

  • 如果我禁用延迟加载强制执行所需的急切加载,它会按预期工作,但如果其他开发人员调用child.Parent.Id而不是child.ParentId尝试做一些新的事情(比如一开始没有考虑的新要求或功能),它会得到一个NullReferenceExceptionif不包括该依赖项,这很糟糕……但这将是一个“快速错误”,并且可以立即修复。

  • 如果我启用延迟加载,每次访问数据库时,访问child.Parent.Id而不是将以对数据库的独立查询结束。child.ParentId它不会失败,但更糟糕的是因为没有错误,只是性能下降,所有代码都应该审查。

我对这两种解决方案中的任何一种都不满意。

  • 我不高兴拥有包含 null 或空集合的实体,而实际上,这不是真的。

  • 我不喜欢让 EF 随时对数据库执行任意查询。如果可能的话,我想在一次拍摄中获得所有信息。

所以,我想出了几个可能的解决方案,包括禁用延迟加载强制加载,但不确定哪个更好:

  • 我可以创建一个EntityBase类,其中包含表中没有集合的数据,因此无法访问它们。以及包含关系的具体实现,问题是你没有太多的灵活性,因为 C# 不允许多重继承。

  • 我可以创建接口来“屏蔽”隐藏在该方法调用中不可用的属性的对象。例如,如果我有一个User.Roles属性,为了向所有用户显示一个网格,我不需要解析该.Roles属性,因此我可以创建一个不包含此类属性的接口“IUserData”。

但如果这项额外的工作值得,我不这样做,也许快速NullReferenceException指示“此属性尚未加载”就足够了。

如果属性是虚拟的并且没有被覆盖/设置,是否可以抛出特定的异常类型?

你用什么方法?

谢谢。

4

1 回答 1

8

在我看来,您试图保护开发人员免于了解他们在访问数据时正在做什么以及它可能对性能产生的影响 - 这可能会导致不必要的复杂 API,其中包含许多帮助类、基类、接口, ETC。

如果开发人员使用user.MiddleName.Trim()and MiddleNameisnull他得到 aNullReferenceException并且做错了什么,要么没有检查,要么没有null确保MiddleName将 a 设置为一个值。当他访问user.Roles并获得 a时也是NullReferenceException如此:他没有检查null或没有调用加载Roles用户的 API 的适当方法。

我想说:解释导航属性是如何工作的,如果开发人员不遵守规则,必须明确请求它们,并让应用程序崩溃。他需要理解错误并改正它。

作为帮助,您可以在 API 中以某种方式显式加载相关数据,例如使用以下方法:

public User GetUser(int userId);
public User GetUserWithRoles(int userId);

或者:

public User GetUser(int userId, params Expression<Func<User,object>>[] includes);

可以这样调用:

var userWithoutRoles = layer.GetUser(1);
var userWithRoles = layer.GetUser(2, u => u.Roles);

您还可以利用显式加载而不是延迟加载来强制开发人员在他们想要加载导航属性而不只是访问该属性时调用方法。

两个补充说明:

...延迟加载...每次访问时都会以对数据库的独立查询结束。

...尚未加载”来完成此操作。如果导航属性已在同一上下文中加载,则再次访问该属性不会触发对数据库的查询。

如果可能的话,我想在一次拍摄中获得所有信息。

多个查询不一定会导致性能比一个带有很多Includes 的查询更差。事实上,复杂的急切加载会导致在线上的数据倍增,并使实体实现非常耗时且比多个延迟或显式加载查询慢。(这是一个示例Include,通过将查询的性能从带有 s 的单个查询更改为不带s 的超过 1000 个查询,查询的性能提高了 50 倍Include。)精髓是:您无法可靠地预测在特定情况下最佳加载策略是什么不衡量绩效(如果绩效在那种情况下很重要)。

于 2012-09-02T20:43:58.367 回答