2016-06-09 135 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; 
    }