【问题标题】:Using DbContext instance inside the entity class在实体类中使用 DbContext 实例
【发布时间】:2019-03-07 23:06:35
【问题描述】:

最近我开始学习 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 类有关系,它们都在使用来自实体框架的迁移创建的数据库中具有适当的表。 LoadOrders() 方法的目的只是在需要时为当前用户加载相关实体。

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

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

或者也许 LoadOrders() 方法的代码应该位于一些额外的类中,比如 UserHelper 将与 User 实体一起使用。

【问题讨论】:

  • 我认为最好的选择是在客户端代码中使用它。
  • 应该是有效的。创建数据库结构和读​​/写数据库的问题是两个独立的问题,应该受到更松散耦合设计的威胁。如果您想测试您的代码,我还建议您使用一些设计模式,例如存储库模式。
  • 实际上领域驱动设计建议做这样的事情来避免“贫血类”。而是在实例化 DbContext 时,尝试将其传递给方法。看看这篇文章。 thereformedprogrammer.net/…

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


【解决方案1】:

您应该避免使用这样的方法,因为用户将由一个 DbContext 加载,而它的订单将与另一个已处置的上下文相关联。当您去更新用户时,您将面临错误或重复订单,或者在保存之前将订单(和其他子实体)重新关联到上下文的混乱业务。如果订单被映射到用户并且有人去向.Include(x =&gt; x.Orders) 编写代码,那么毫无疑问会造成混乱给你。

此类问题通常源于混淆了实体的范围/生命周期与加载它们的上下文范围。例如,在一种方法中使用 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() 获取匿名类型,使用.ToList()/.Single()/等让 EF 将它们具体化为 POCO 实例。然后使用 Linq2Object 填充您的视图模型。

使用按需实体和视图模型/DTO 来往来回数据避免了很多与实体有关的麻烦。如果做得好,EF 可以非常快速地提取这些数据,并且避免了性能缺陷,例如在序列化期间触发延迟加载。这意味着当您完成视图模型时,您将需要重新加载实体以应用更改。简单地使用实体然后让 EF 神奇地重新附加它们并保持更改似乎更有意义,但是如果需要,您的视图模型将拥有通过 ID 快速获取该实体所需的所有信息,并且您需要考虑案例从您第一次检索实体到您准备更改它,数据可能发生了变化。

【讨论】:

    猜你喜欢
    • 2011-07-10
    • 1970-01-01
    • 2017-04-07
    • 2018-05-30
    • 2013-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多