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

mar*_*nes 5 .net c# 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; }
}
Run Code Online (Sandbox Code Playgroud)

然后我有一个EF查询返回'all active' Rules,我需要它.Include Exclusionsfor each 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

我尝试在其中使用.Where .Include()试图仅包含相关Exclusions(基于instanceId)但发现你不能这样做.我在周围寻找,并找到了一些人们使用匿名类型的例子,但是当我在这里做的时候,我无法逐步建立查询.

所以,我不知道如何实现这个目标,因为我真的不希望每个人Exclusion都返回'每个' Rule,当我不需要每个人都Exclusion返回时.

Jon*_*nan 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 ...
Run Code Online (Sandbox Code Playgroud)

Wiki:EF+ 查询 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:回答子问题#1

如何在前面的示例中使用 ToListAsync

您只需等待第一个列表

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

编辑:回答子问题#2

如何对规则执行 Skip、Take、OrderBy

你和平时一样

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();
Run Code Online (Sandbox Code Playgroud)