在实体类中使用DbContext实例

时间:2018-10-02 19:03:24

标签: c# .net entity-framework design-patterns entity-framework-core

最近我已经开始学习Entity Framework Core,并且很好奇在实体类中使用DbContext实例是否合适。

示例代码:

class User {
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Order> Orders { get; set; }

    public void LoadOrders() {
        using (var context = new StoreContext()) {
            Orders = context.Orders             
                .Where(x => x.UserId == Id)
                .ToList();
        }
    }
}

用户实体与Order类有关系,它们都在使用从Entity Framework进行的迁移创建的数据库中具有适当的表。 LoadOrders()方法的目的只是在必要时为当前用户加载相关实体。

现在我想知道这是否是一种有效的方法?

或者也许我应该在加载父对象的同时总是加载相关的实体? (例如.Include()。ThenInclude())

或者也许LoadOrders()方法的代码应该位于其他类中,例如UserHelper,它将与User实体一起使用。

1 个答案:

答案 0 :(得分:1)

您应该避免使用这样的方法,因为User将由一个DbContext加载,而其订单将与另一个已处置的上下文关联。当您更新用户时,您将面临错误或重复的订单,或者在保存之前将订单(和其他子实体)重新关联到上下文的繁琐工作。如果将订单映射到用户并且有人去.Include(x => x.Orders)写代码,那么毫无疑问,如果您将相关实体与EF完全分离并依靠按需负载,那么您将失去很多功能EF给你的。

这样的问题通常是由于混淆了实体的范围/寿命与从中加载它们的上下文的范围。例如,使用一种方法使用DbContext加载实体,返回它们,然后再决定要访问相关的实体,但是DbContext被废弃了。我可以建议使用的最简单方法是采用POCO视图模型,并确保实体永远不会退出其DbContext的范围,只有视图模型才会这样做。这样,您可以雕刻视图模型结构来表示所需的数据,然后使用实体及其引用通过.Select()填充这些视图模型,而不必担心延迟加载或急切加载。

例如:

using (var context = new StoreContext())
{
  var userViewModel = context.Users.Where(x => x.UserId == userId)
    .Select(x => new UserViewModel 
    {
      UserId = x.UserId,
      UserName = x.UserName,
      Orders = x.Orders
        .Where(o => o.IsActive)
        .Select( o => new OrderViewModel
        {
          OrderId = o.OrderId,
          OrderNumber = o.OrderNumber
          Price = o.OrderItems.Sum(i => i.Price)
        }).ToList()
     }).SingleOrDefault();
   return userViewModel;
}

Automapper可以协助映射实体以查看模型。它不是一对一的树形结构图,而是对齐视图模型以表示视图所需的数据,然后用实体结构填充它。您只需要小心一点,就只能从实体中提取数据和受支持的聚合方法,因为它们将传递给SQL,因此.Select中没有.Net或自定义函数。让视图模型接受原始值并提供替代属性以执行格式设置,或者使用.Select()获取匿名类型,让EF通过.ToList() / .Single() / etc将其具体化为POCO实例。然后从使用Linq2Object的视图模型中填充视图模型。

与按需实体配合使用并查看模型/ DTO来回处理数据避免了实体带来的大量麻烦。如果做得正确,EF可以极其快速地提取此数据,并且可以避免性能方面的陷阱,例如在序列化过程中触发懒惰的负载。这意味着在使用完View Model之后,您将需要重新加载实体以应用更改。简单地使用实体,然后让EF神奇地重新连接它们并保留更改似乎更有意义,但是您的视图模型将具有所需的所有信息,以便在需要时通过ID快速获取该实体,并且您需要考虑各种情况在您首次检索该实体与准备进行更改之间的时间里,数据可能已更改。