2016-06-09 47 views
5

時,我有以下2類:如何申請WHERE條件EF .INCLUDE()建立EF查詢

public class Rule 
{ 
    public int Id { get; set; } 
    public string RuleValue { get; set; } 
    public bool IsActive { get; set; } 
    public SharedRuleType RuleType { get; set; } 
    public List<Exclusion> Exclusions { get; set; } 
} 

public class Exclusion 
{ 
    public int Id { get; set; } 
    public int InstanceId { get; set; } 
    public int SiteId { get; set; } 
    [ForeignKey("RuleId")] 
    public int RuleId { get; set; } 
    public Rule Rule { get; set; } 
} 

然後我有一個EF查詢帶回「所有活動」 Rules,和我需要它。包括Exclusions爲每個Rule(如果有的話)但只有Exclusions已被分配了指定的InstanceId。因此,正在對Exclusions屬性進行篩選,而不是篩選出Rules

我也有一些條件,因爲我建立我需要考慮的EF查詢。

這是我的時刻查詢:

public async Task<List<Rule>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm) 
{ 
    using (var context = new MyDbContext()) 
    { 
     var query = context.Set<Rule>() 
      .Include(r => r.Exclusions) // *** Currently returns ALL exclusions but I only want ones where InstanceId == instanceId(param) *** 
      .Where(r => r.IsActive); 

     if (!string.IsNullOrEmpty(searchTerm)) 
     { 
      query = query.Where(r => r.RuleValue.Contains(searchTerm)); 
     } 

     if (ruleType != SharedRuleType.None) 
     { 
      query = query.Where(r => r.RuleType == ruleType); 
     } 

     return await query.ToListAsync(); 
    } 
} 

我試過,試圖在.Include()內應用。哪裏,只包括相關Exclusions(基於instanceId),但發現你不能做那。我在周圍尋找並發現了一些人們使用匿名類型的例子,但是當我在這裏做的時候,我無法在逐個構建查詢時得到這個工作。

所以,我不知道我該如何實現這一點,因爲我真的不希望每個都返回'every'Exclusion,當我不需要每個Exclusion都返回時。

+2

有時,它只是更容易執行正確的SQL。 –

+0

你解決了嗎? –

+0

@VinodKumar不,還沒有。我不認爲下面的Vitaliy的解決方案會起作用,因爲它不會返回「全部」規則,它會根據排除規則篩選出一些。 – marcusstarnes

回答

2

Include方法不能使用像您嘗試的過濾器。

解決方案#1

免責聲明:我是這個項目的所有者Entity Framework Plus

EF +查詢IncludeFilter功能允許過濾相關實體。

public async Task<List<Rule>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm) 
{ 
    using (var context = new MyDbContext()) 
    { 
     var query = context.Set<Rule>() 
      .IncludeFilter(r => r.Exclusions.Where(x => x.InstanceId == instanceId)) 
      .Where(r => r.IsActive); 

     // ... code ... 

百科:EF+ Query IncludeFilter

解決方案#2

另一種技術是通過使用投影(這是我的圖書館引擎蓋下做)

public async Task<List<Rule>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm) 
{ 
    using (var context = new MyDbContext()) 
    { 
     var query = context.Set<Rule>() 
      .Where(r => r.IsActive); 

     if (!string.IsNullOrEmpty(searchTerm)) 
     { 
      query = query.Where(r => r.RuleValue.Contains(searchTerm)); 
     } 

     if (ruleType != SharedRuleType.None) 
     { 
      query = query.Where(r => r.RuleType == ruleType); 
     } 


     // ToListAsync has been removed to make the example easier to understand 
     return query.Select(x => new { Rule = x, 
             Exclusions = x.Exclusions.Where(e => e.InstanceId == instanceId) 
        }) 
      .ToList() 
      .Select(x => x.Rule) 
      .ToList(); 
    } 
} 

編輯:回答子問題#1

如何使用ToListAsync與前面的例子

您只需等待第一列表

return (await query.Select(x => new { Rule = x, 
           Exclusions = x.Exclusions.Where(e => e.InstanceId == instanceId) 
      }) 
    .ToListAsync()) 
    .Select(x => x.Rule) 
    .ToList(); 

編輯:回答小問題#2

如何按規則執行跳過,取出,排序By

Yo ü做同樣與平時一樣

return (await query.Take(15) 
        .Skip(5) 
        .OrderBy(x => x.RuleId) 
        .Select(x => new { Rule = x, 
              Exclusions = x.Exclusions.Where(e => e.InstanceId == instanceId) 
           }) 
    .ToListAsync()) 
    .Select(x => x.Rule) 
    .ToList(); 
+0

感謝您的可能解決方案。有一個問題,在解決方案2中,我將如何將.Skip,.Take和.OrderBy合併到查詢中,以便在一次訪問數據庫時執行該過濾,因爲事實證明我需要這樣做。 – marcusstarnes

+0

另外,我將如何實施解決方案#2與等待/異步? – marcusstarnes

+0

您想在哪個實體(規則或排除)中執行Skip,Take,OrderBy? –

0

編輯根據您的評論,您需要爲表進行LEFT JOIN請求。

這裏是你的新方法

public class RuleModel 
{ 
    public Rule Rule { get; set; } 
    public IEnumerable<Exclusion> Exclusions { get; set; } 
} 

public async Task<List<RuleModel>> GetRules(int instanceId, SharedRuleType ruleType, string searchTerm) 
{ 
    using (var context = new MyDbContext()) 
    { 
     var query = context.Set<Rule>() 
      .Where(r => r.IsActive); 

     if (!string.IsNullOrEmpty(searchTerm)) 
     { 
      query = query.Where(r => r.RuleValue.Contains(searchTerm)); 
     } 

     if (ruleType != SharedRuleType.None) 
     { 
      query = query.Where(r => r.RuleType == ruleType); 
     } 

     // That statement do LEFT JOIN like: 
     // FROM Rules 
     // LEFT OUTER JOIN Exclusions ON ([Rules].[Id] = [Exclusions].[RuleId]) AND ([Exclusions].[InstanceId] = @instanceId) 
     var ruleExclusionQuery = query.Select(rule => new RuleModel { Rule = rule, Exclusions = rule.Exclusions.Where(e => e.InstanceId == instanceId) }); 
     var ruleList = await ruleExclusionQuery.ToListAsync(); 
    } 
} 

變種正如你可以看到現在你不能只返回規則列表中,如果你需要排除了。你必須爲此返回新課程。並且不要使用結果中的Rule.Exclusions,因爲它會向數據庫發出延遲請求並加載所有相關的排除項。

.Include(r => r.Exclusions) 

不再需要。

+0

這是不是會過濾掉我的一些規則(只有返回規則的地方有與實例匹配的排除)?無論是否有排除項目(以及排除項目),我都需要「全部」規則。它只是包含的需要過濾的排除(如果有的話)。 – marcusstarnes

+0

@marcusstarnes我更新了代碼。請檢查它是否符合你的要求。 –

0

Conditional include尚未由EF團隊 實現這仍是EF團隊工作項目,你可以投票here

注意它目前不可能過濾哪些相關的實體被加載。包含將始終引入所有相關實體。

如果要篩選包含語句,則需要在EF上使用投影。

using (var context = new MyDbContext()) 
{ 

    Expression<Func<Rules, bool>> whereCondition; 

    if (!string.IsNullOrEmpty(searchTerm)) 
    { 
     whereCondition= x.RuleValue.Contains(searchTerm)); 
    } 

    var query = context.Rules 
         .Where(whereCondition) 
         .Select(x=> new 
         { 
         rules = x, 
         exclustions = x.Exclusions.Where(secondCondition).ToList() 
         }.ToList(); 
} 

如果你想表達的是工作像IQuerable

你可以試試這個,但沒有測試

if (!string.IsNullOrEmpty(searchTerm)) 
    { 
     whereCondition= x.RuleValue.Contains(searchTerm); 
    } 

    if (ruleType != SharedRuleType.None) 
    { 
     whereCondition= x.RuleType ==ruleType; 
    } 
    //Ugly work around is 
    if (!string.IsNullOrEmpty(searchTerm) && ruleType != SharedRuleType.None) 
    { 
     whereCondition= x.RuleValue.Contains(searchTerm) && x.RuleType ==ruleType; 
    }