更多地阅读 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 时,使用SingleOrDefault。 FirstOrDefault 仅在您期望 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 和序列化异常。