实体框架中的Linq查询优化

时间:2019-06-06 14:18:54

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

我有以下查询,我正在寻找的是优化where子句。我不喜欢多次使用FirstOrDefault。是否可以用Join或其他方法来实现它,这样我将只能访问一次FirstOrDefault

var promos = await this._context
                       .SinglePromotions
                       .Include(p => p.Rewards)
                       .Include(p => p.InAppProduct)
                       .Include(p => p.PlayerSinglePromotions)
                       .ThenInclude(sp => sp.Player)
                       .ThenInclude(pl => pl.Purchases)
                       .Where(p => p.MinimumPlayerLevel <= Player.Level && 
                                   p.Placement == placement && 
                                   p.IsActive && 
                                   (p.PlayerSinglePromotions.All(sp => sp.PlayerID != Player.ID) || 
                                    (p.PlayerSinglePromotions.FirstOrDefault(sp => sp.PlayerID == Player.ID).PurchaseAmount < p.PurchaseLimit)) &&
                                    (p.PlayerSinglePromotions.All(sp => sp.PlayerID != Player.ID) || (p.PlayerSinglePromotions.FirstOrDefault(sp => sp.PlayerID == Player.ID).Player.Purchases.Count <= p.MaxInAppPurchasesByPlayer))
                       )
                       .ToListAsync();

2 个答案:

答案 0 :(得分:1)

嗯,也许有更好的解决方案,但是您可以使用扩展名FirstOrDefault代替使用Any的条件:

var promos = await this._context.SinglePromotions
.Include(p => p.Rewards)
.Include(p => p.InAppProduct)
.Include(p => p.PlayerSinglePromotions)
    .ThenInclude(sp => sp.Player)
    .ThenInclude(pl => pl.Purchases)
.Where(
    p => p.MinimumPlayerLevel <= Player.Level && 
    p.Placement == placement && 
    p.IsActive &&
   (p.PlayerSinglePromotions.All(sp => sp.PlayerID != Player.ID) ||
    p.PlayerSinglePromotions.Any(sp => sp.PlayerID == Player.ID 
                                   && (sp.Player.Purchases.Count <= p.MaxInAppPurchasesByPlayer || sp.PurchaseAmount < p.PurchaseLimit))))
.ToListAsync();

答案 1 :(得分:1)

通过将查询语法与let clause配合使用,可以防止LINQ查询中的长表达式重复。这就像声明包含表达式结果的变量一样。在您的查询中,重复了两个部分,可以在let变量中捕获它们:

var promos = await (
    from p in this._context.SinglePromotions
        .Include(p => p.Rewards)
        .Include(p => p.InAppProduct)
        .Include(p => p.PlayerSinglePromotions)
        .ThenInclude(sp => sp.Player)
        .ThenInclude(pl => pl.Purchases)
    let pspNotOfPlayer = p.PlayerSinglePromotions.All(sp => sp.PlayerID != Player.ID)
    let firstPromotion = p.PlayerSinglePromotions.FirstOrDefault(sp => sp.PlayerID == Player.ID)
    where
        p.MinimumPlayerLevel <= Player.Level 
        && p.Placement == placement 
        && p.IsActive
        && pspNotOfPlayer || firstPromotion.PurchaseAmount < p.PurchaseLimit
        && pspNotOfPlayer || firstPromotion.Player.Purchases.Count <= p.MaxInAppPurchasesByPlayer
    select p
    )
    .ToListAsync();

在许多情况下,这不仅可以提高代码的可读性,而且可以改善生成的SQL,因为可以用CROSS APPLY子句代替重复的子查询。