实体框架依赖项正在加载



我有一个长期悬而未决的问题,即如何避免通过实体框架(版本6,遗憾的是还不是核心(查询的数据出现null错误。

假设您有一个表Employees,它与另一个表(EmployeePayments(有关系(一个员工有许多员工付款(。

在您的Employee域对象上,您创建了一个属性TotalPayments,该属性依赖于您为该对象加载了EmployeePayments

我试图确保每当我进行查询时;包括";依赖关系,例如:

var employees = context.Employees.Include(e => e.EmployeePayments);

问题是,我在这个地方有很多查询(我使用通用存储库模式,所以我从服务库中调用GetAllGetSingle之类的存储库函数(,所以有很多地方需要记住添加include。如果我不包括它们,那么如果使用TotalPayments属性,我将面临出现null异常的风险。

处理这个问题的最佳方法是什么?

注意1:我们有很多表,我真的不想再为每个表使用特定的存储库,我们在很多方面都利用了通用存储库。。。。但我很乐意听到关于替代方案的有力论据:(

注意2:出于性能原因,我没有打开延迟加载,也不打算打开它。

这就是我认为Generic Repository是EF的反模式的原因之一。我使用一个存储库模式,但将其视为一个控制器。例如,CreateOrderController将具有CreateOrderRepository。该存储库将通过IQueryable提供对所有相关实体的访问。像查找等常见的东西都有自己的辅助存储库。使用适合于处理单个实体类型的通用存储库意味着添加对多个存储库的引用来执行特定的操作,并且在尝试加载实体时会遇到这样的问题。有时你想要相关的数据,有时你不想要。简单地在顶级实体中有效地添加方便的方法";"断裂";一个对象应该始终被认为是完整的或可完全的,而不依赖于带来显著性能成本的延迟加载。

让存储库返回IQueryable可以通过控制调用代码如何使用实体来避免许多问题。例如,我没有在实体中放入helper方法,而是需要填充视图模型的代码依赖于Linq来构建视图模型。如果我的视图模型想要为员工支付一笔款项,那么我的存储库返回IQueryable可以执行以下操作:

public IQueryable<Employee> GetEmployeeById(int employeeId)
{
return Context.Employees.Where(x => x.EmployeeId == employeeId);
}

然后在控制器/服务中:

using (var contextScope = ContextScopeFactory.Create())
{
var employeeViewModel = EmployeeRepository.GetEmployeeById(employeeId)
.Select(x => new EmployeeSummaryViewModel
{
EmployeeId = x.EmployeeId,
EmployeeName = x.LastName + ", " + x.FirstName,
TotalPayments = x.Payments.Where(p => p.IsActive).Sum(p => p.Amount)
}).Single();
}

我使用存储库是因为它比DbContext及其DbSets更容易模拟。对于同步代码,我只需要mock来填充并返回List<Employee>().AsQueryable()。对于异步代码,我需要为异步列表添加一个包装器。

这种模式可能违背了存储库的更传统的视图和调用代码需要"关注"的分离;知道";关于实体,EF主义被泄露。然而,无论你试图用什么方法来合理化,以绕过试图";隐藏";EF在存储库后面,要么你会留下非常低效的代码,其中存储库返回预先填充的DTO,要么包含几十个几乎相同的方法来返回不同的DTO(或者更糟的是,不同完整度的实体(,要么你正在向你的方法中添加复杂性,比如传递魔术串或表达式树,告诉EF如何过滤、如何排序、包括什么,传递表达式或字符串需要调用代码";知道";关于实体和泄漏EF限制。(传入的表达式/字符串仍然必须能够被EF最终理解(

因此,对于您的项目的当前状态,这可能不是一个可行的答案,但值得研究的是,在不使用Generic模式拆分存储库的情况下,是否可以更好地管理您对存储库的依赖,和/或利用EF出色的IQueryable/Linq功能,让您的控制器/服务将实体投影到视图模型/DTO中,而不是将这些reduce元素嵌入实体本身。

最新更新