简体   繁体   English

Ef 返回 null,但实体存在

[英]Ef returning null, but entity exists

I'm trying to load some user info in code, but EF returns null.我正在尝试在代码中加载一些用户信息,但 EF 返回 null。

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

Variable user reporting 1 wallet, but when I'm trying to get it in code above it returns null.可变用户报告 1 个钱包,但是当我尝试在上面的代码中获取它时,它返回 null。

Are there some ways to solve this problem?有一些方法可以解决这个问题吗?

Read more into Linq expressions rather than relying on Find .阅读更多 Linq 表达式,而不是依赖Find You can be running into issues if your relationships between entities are not defined as virtual which would prevent EF from lazy loading them.如果实体之间的关系未定义为virtual关系,您可能会遇到问题,这会阻止 EF 延迟加载它们。

The issue with using .Find() is that it will return the entity if it exists, however, attempting to access any related property that the DbContext isn't already aware of will require a lazy load call.使用.Find()的问题在于,如果实体存在,它将返回实体,但是,尝试访问 DbContext 尚未意识到的任何相关属性将需要延迟加载调用。 This can be easily missed if lazy loading is disabled, or the member isn't virtual , and it can be a performance issue when it is enabled.如果延迟加载被禁用,或者成员不是virtual ,这很容易被忽略,并且在启用时可能是性能问题。

Instead, Linq can allow you to query through the object graph directly to get what you want:相反,Linq 可以让你通过 object 图表直接查询得到你想要的:

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...
}

This assumes that there will be only 1 wallet for the specified currency per user.这假设每个用户只有 1 个指定货币的钱包。 When expecting 0 or 1, use SingleOrDefault .当期望 0 或 1 时,使用SingleOrDefault Use FirstOrDefault only when you are expecting 0 or many, want the "first" one and have specified an OrderBy clause to ensure the first item is predictable.仅当您期望 0 或多个,想要“第一个”并指定OrderBy子句以确保第一个项目是可预测的时,才使用FirstOrDefault

This will result in a query per user.这将导致每个用户的查询。 To accomplish with 1 query for all users:为所有用户完成 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();

From this I would consider expanding the wallets SelectMany with a Select for the details in the Wallet you actually care about rather than a reference to the entire wallet entity.因此,我会考虑使用Select扩展钱包SelectMany ,以获取您真正关心的钱包中的详细信息,而不是对整个钱包实体的引用。 This has the benefit of speeding up the query, reducing memory use, and avoids the potential for lazy load calls tripping things up if Wallet references any other entities that get touched later on.这样做的好处是可以加快查询速度,减少 memory 的使用,并避免在 Wallet 引用稍后涉及的任何其他实体时潜在的延迟加载调用。

For example if you only need the IdWallet, WalletName, TypeCurrency, and Balance:例如,如果您只需要 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
          }) // ...

From there you can foreach to your heart's content without extra queries:从那里你可以在没有额外查询的情况下foreach你的内心需求:

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

If you want to return the wallet details to a calling method or view or such, then you cannot use an anonymous type for that ( new { } ).如果您想将钱包详细信息返回给调用方法或视图等,则不能为此使用匿名类型( new { } )。 Instead you will need to define a simple class for the data you want to return and use Select into that.相反,您需要为要返回的数据定义一个简单的 class 并在其中使用Select Ie new WalletDTO { IdWallet = w.IdWallet, //... } Even if you use Entities, it is recommended to reduce these into DTOs or ViewModels rather than returning entities.new WalletDTO { IdWallet = w.IdWallet, //... }即使你使用 Entities,也建议将这些减少为 DTO 或 ViewModel,而不是返回实体。 Entities should not "live" longer than the DbContext that spawned them otherwise you get all kinds of nasty behaviour popping up like ObjectDisposedException and serialization exceptions.实体不应该比产生它们的 DbContext “存活”更长的时间,否则会出现各种令人讨厌的行为,例如ObjectDisposedException和序列化异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM