【问题标题】:Ef returning null, but entity existsEf 返回 null,但实体存在
【发布时间】:2019-11-18 16:09:46
【问题描述】:

我正在尝试在代码中加载一些用户信息,但 EF 返回 null。

foreach (var user in allAutoUsers)
{
    Wallet wallet = db.CabinetUsers
                      .Find(user.IdCabinetUser)?
                      .Wallets
                      .FirstOrDefault(x => x.TypeCurrency == currency);
}

变量用户报告 1 个钱包,但是当我尝试在上面的代码中获取它时,它返回 null。

有没有办法解决这个问题?

【问题讨论】:

标签: c# .net database entity-framework linq


【解决方案1】:

更多地阅读 Linq 表达式,而不是依赖 Find。如果实体之间的关系未定义为 virtual,您可能会遇到问题,这会阻止 EF 延迟加载它们。

使用.Find() 的问题在于,如果实体存在,它将返回实体,但是,尝试访问 DbContext 尚未意识到的任何相关属性将需要延迟加载调用。如果延迟加载被禁用,或者成员不是virtual,则很容易错过这一点,并且在启用时可能会出现性能问题。

相反,Linq 可以让你通过对象图直接查询得到你想要的:

foreach (var user in allAutoUsers)
{
    Wallet wallet = db.CabinetUsers
       .Where(x => x.IdCabinetUser == user.IdCabinetUser)
       .SelectMany(x => x.Wallets)
       .SingleOrDefault(x => x.TypeCurrency == currency);

   // do stuff with wallet...
}

这假设每个用户只有 1 个指定货币的钱包。当期望 0 或 1 时,使用SingleOrDefaultFirstOrDefault 仅在您期望 0 或多个、想要“第一个”并指定 OrderBy 子句以确保第一个项目是可预测的时使用。

这将导致每个用户进行一次查询。为所有用户完成 1 个查询:

var userIds = allAutoUsers.Select(x => x.IdCabinetUser).ToList();
var userWallets = db.CabinetUsers
       .Where(x => userIds.Contains(x.IdCabinetUser))
       .Select(x => new 
       {
          x.IdCabinetUser,
          Wallet = x.SelectMany(x => x.Wallets)
            .SingleOrDefault(x => x.TypeCurrency == currency);
       }).ToList();

据此,我会考虑使用 Select 扩展钱包 SelectMany,以获取您真正关心的钱包中的详细信息,而不是对整个钱包实体的引用。这样做的好处是可以加快查询速度、减少内存使用,并避免在 Wallet 引用稍后涉及的任何其他实体时延迟加载调用导致问题发生的可能性。

例如,如果您只需要 IdWallet、WalletName、TypeCurrency 和 Balance:

// replace this line from above...
          Wallet = x.SelectMany(x => x.Wallets)

// with ...
          Wallet = x.SelectMany(x => x.Wallets.Select(w => new 
          { 
              w.IdWallet,
              w.WalletName,
              w.TypeCurrency,
              w.Ballance
          }) // ...

您可以从那里foreach 尽情享受,无需额外查询:

foreach ( var userWallet in userWallets)
{
   // do stuff with userWallet.Wallet and userWallet.IdCabinetUser.
}

如果您想将钱包详细信息返回给调用方法或视图等,那么您不能为此使用匿名类型(new { })。相反,您需要为要返回的数据定义一个简单的类,并在其中使用Select。 IE。 new WalletDTO { IdWallet = w.IdWallet, //... } 即使您使用实体,也建议将它们缩减为 DTO 或 ViewModel,而不是返回实体。实体的“存活”时间不应超过产生它们的 DbContext,否则会出现各种令人讨厌的行为,例如 ObjectDisposedException 和序列化异常。

【讨论】:

  • 谢谢,我从钱包中删除了延迟加载,因为它们里面有更多相关的实体
猜你喜欢
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-30
  • 1970-01-01
相关资源
最近更新 更多