我有以下两个班级:
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}}返回。
答案 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 ...
解决方案#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;
}