如何在构建EF查询时将WHERE条件应用于EF .Include()

时间:2016-06-09 07:37:31

标签: c# .net entity-framework

我有以下两个班级:

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(如果有的话)但仅{ {1}}已分配指定的Exclusions。因此,过滤是针对InstanceId属性进行的,而不是过滤掉Exclusions

我还有一些条件,因为我构建了我需要考虑的EF查询。

目前我的查询是:

Rules

我尝试在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(); } } 中应用.Where以尝试仅包含相关的.Include()(基于Exclusions),但发现您不能这样做。我在周围寻找并找到了一些人们使用过匿名类型的例子,但是当我在这里一块一块地构建查询时,我无法解决这个问题。

所以,我不知道如何实现这一目标,因为当我不需要每个instanceId时,我真的不想为每个Exclusion返回'Rule Exclusion 1}}返回。

3 个答案:

答案 0 :(得分:2)

Include方法无法使用您尝试过的过滤器。

解决方案#1

免责声明:我是该项目的所有者Entity Framework Plus

EF + Query 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

如何执行Skip,Take,OrderBy on Rule

你做的和往常一样

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

答案 1 :(得分: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,因为它会向DB发出延迟请求并加载所有相关的排除项。

.Include( r => r.Exclusions )

不再需要了。

答案 2 :(得分:0)

  EF团队尚未实施

Conditional include   这仍然是EF团队的一个工作项目,您可以投票here

请注意,目前无法过滤加载了哪些相关实体。包含将始终引入所有相关实体。

如果要过滤include语句,则需要在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;
    }