2016-06-09 14 views
5

ho le seguenti classi: 2Come applicare condizione WHERE per EF .include() quando si costruisce fino interrogazione 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; } 
} 

Ho poi ho una query EF che riporta 'tutti attivi' Rules, e I necessario. Includere Exclusions per ogni Rule (se ce ne sono) MA solo per Exclusions a cui è stato assegnato lo InstanceId specificato. Pertanto, il filtro viene eseguito in base alla proprietà Exclusions anziché filtrare Rules.

Ho anche alcune condizioni durante la creazione della query EF che devo prendere in considerazione.

Ecco la mia domanda in questo momento:

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(); 
    } 
} 

ho cercato di applicare un .Dove all'interno del .Include() nel tentativo di includere solo il relativo Exclusions (sulla base di instanceId), ma scoperto non si può fare quella. Ho dato la caccia e ho trovato alcuni esempi in cui la gente aveva usato un tipo anonimo, ma non riuscivo a farlo funzionare quando ho costruito la query pezzo per pezzo come sto facendo qui.

Quindi, non so come riuscirò a realizzare questo dato che in realtà non voglio tornare 'ogni' Exclusion per ogni Rule, quando non ho bisogno di restituire ogni Exclusion.

+2

a volte è solo più facile da eseguire una corretta SQL. –

+0

Hai risolto? –

+0

@VinodKumar No, non ancora. Non credo che la soluzione di Vitaliy riportata sotto funzionerà in quanto non restituirà le regole "tutte", ne avrà filtrato alcune in base alle esclusioni. – marcusstarnes

risposta

2

Il metodo Include non può utilizzare un filtro come si è tentato.

Soluzione # 1

Diniego: Sono il proprietario del progetto Entity Framework Plus

funzione EF + Query IncludeFilter permette di filtrare entità correlate.

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

Wiki: EF+ Query IncludeFilter

Soluzione # 2

Un'altra tecnica è quello di utilizzare la proiezione (che è ciò che la mia libreria fare sotto il cofano)

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(); 
    } 
} 

EDIT: Risposta Domande secondarie n. 1

Come utilizzare ToListAsync con l'esempio precedente

Devi semplicemente attendere per il primo elenco

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

EDIT: risposta sotto-domande # 2

Come eseguire Salta, Take, OrderBy sulla regola

Yo u fare lo stesso come si fa normalmente

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

Grazie per le possibili soluzioni. Una domanda, con la soluzione n. 2, come dovrei incorporare .Skip, .Take e .OrderBy nella query in modo da eseguire tale filtraggio nell'unico viaggio nel DB, in quanto risulta che ho bisogno di questo. – marcusstarnes

+0

Inoltre, come implementare la Soluzione n. 2 con Await/Async? – marcusstarnes

+0

Su quale entità (regola o esclusione) si desidera eseguire Salta, Take, OrderBy? –

0

Modifica In base al tuo commento, è necessario fare richiesta che rende LEFT JOIN per le tabelle.

Ecco variante per il nuovo metodo di

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(); 
    } 
} 

Come si può vedere ora non si può semplicemente tornare Nell'elenco Regola se avete bisogno di troppo Esclusioni. Devi restituire una nuova classe per quello. E non utilizzare Rule.Exclusions dal risultato perché farà una richiesta lazy al DB e caricherà tutte le esclusioni correlate.

.Include(r => r.Exclusions) 

non è più necessario.

+0

Non escludo comunque alcune delle mie regole (solo restituendo regole in cui esistono esclusioni che corrispondono all'istanza)? Ho bisogno di "tutte" le regole indipendentemente dal fatto che abbiano esclusioni (e quali esclusioni esse siano). Sono solo le esclusioni incluse (se presenti) che devono essere filtrate. – marcusstarnes

+0

@marcusstarnes Ho aggiornato il codice. Per favore, controlla se corrisponde per te. –

0

Conditional include non è ancora implementata da EF squadra Questo è ancora un elemento di lavoro in team di EF, si poteva votare here

Si noti che non è attualmente possibile filtrare quali entità correlate sono caricate. Include includerà sempre tutte le entità correlate.

Se si desidera filtrare l'istruzione di inclusione è necessario utilizzare la proiezione su 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(); 
} 

Se si desidera l'espressione di essere un'opera come IQuerable

Si potrebbe provare questo, ma non testati

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; 
    } 
Problemi correlati