这似乎非常简单,但我似乎无法解决。使用EF Core,我的DbSet<Rule> Rules
中有一个DbContext
。
public class Rule
{
public int Id { get; set; }
public string Raw { get; set; }
}
我正在尝试编写一个查询,给定IEnumerable<string> lines
,将Rule
中的所有DbSet
都给我,其中Raw
的值是其中的一个元素lines
(完全匹配,而不是值的子字符串)。
一段时间以来,我一直在使用类似的东西:
private IQueryable<Rule> GetExistingRules() =>
dbContext.Rules.Where(r => lines.Contains(r.Raw));
但是,从那以后,我发现(我认为)这没有达到我的期望。 (此方法之后,立即为当前不存在的Rule
的所有元素插入新的lines
。我得到的重复的Rule
具有相同的Raw
值。 ..)我想我需要使用.Intersect()
?
我尝试对每个this使用自定义的EqualityComparer,但是会引发异常。
private IQueryable<Rule> GetExistingRules()
{
var lineRules = lines.Select(l => new Rule {Raw = l});
return dbContext.Rules.Intersect(lineRules, new RuleRawEqualityComparer());
}
private class RuleRawEqualityComparer : IEqualityComparer<Rule>
{
public bool Equals(Rule x, Rule y) => x?.Raw == y?.Raw;
...
}
无法解析表达式 '值(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1 [FilterLists.Data.Entities.Rule])。Intersect(__ p_0, __p_1)':目前不支持方法'System.Linq.Queryable.Intersect'的重载。
撰写此查询的最佳方法是什么?请注意,它是由DbContext
交互组成的链,因此我更喜欢将返回类型保留为IQueryable
以启用EF的惰性查询组合。
更新:有关为何我怀疑Contains()
方法无效的更多信息:
这是使用查询的类。我看到类似下面的异常,因为数据库中的Raw
列具有唯一的约束。我认为我的逻辑(在Except()
中使用CreateNewRules()
可以防止Rules
中的任何行具有重复的Raw
值,但也许我的问题出在其他地方...
public class SnapshotBatch
{
private readonly FilterListsDbContext dbContext;
private readonly IEnumerable<string> lines;
private readonly Data.Entities.Snapshot snapEntity;
public SnapshotBatch(FilterListsDbContext dbContext, IEnumerable<string> lines,
Data.Entities.Snapshot snapEntity)
{
this.dbContext = dbContext;
this.lines = lines;
this.snapEntity = snapEntity;
}
public async Task SaveAsync()
{
var existingRules = GetExistingRules();
var newRules = CreateNewRules(existingRules);
dbContext.Rules.AddRange(newRules);
var rules = existingRules.Concat(newRules);
AddSnapshotRules(rules);
await dbContext.SaveChangesAsync();
}
private IQueryable<Rule> GetExistingRules() =>
dbContext.Rules.Where(r => lines.Contains(r.Raw));
private List<Rule> CreateNewRules(IQueryable<Rule> existingRules) =>
lines.Except(existingRules.Select(r => r.Raw))
.Select(r => new Rule {Raw = r})
.ToList();
private void AddSnapshotRules(IQueryable<Rule> rules) =>
snapEntity.AddedSnapshotRules
.AddRange(rules.Select(r => new SnapshotRule {Rule = r}));
}
异常StackTrace中的代码段(其中'### Meebo:AdElement.Root'是Raw
表中Rules
的样本值):
FilterLists.Services.Snapshot.Snapshot.TrySaveAsync()在 /home/travis/build/collinbarrett/FilterLists/src/FilterLists.Services/Snapshot/Snapshot.cs:line 43的键“ IX_rules_Raw”的条目“ ### Meebo:AdElement.Root”重复 MySql.Data.MySqlClient.MySqlDataReader.ActivateResultSet(ResultSet resultSet) C:\ projects \ mysqlconnector \ src \ MySqlConnector \ MySql.Data.MySqlClient \ MySqlDataReader.cs:line 93 at
更新2 :我相当确定我在Contains()
上遇到的问题是由于this issue带有active PR。因为我的字符串具有各种特殊字符,所以我认为它们在传递到Contains()
时无法正确转义,但似乎可以作为Join()
中的参数正确转义。
答案 0 :(得分:2)
不要忘记,当将linQ与EFCore和IQueryable一起使用时,它会转换Sql语句中的c#代码。
您尝试过吗?
var query = from rule in dbContext.Rules
join line in lines
on rule.Raw equals line
select rule;
答案 1 :(得分:2)
您写道:
从DbSet中获取所有规则,其中Raw值是行中的元素(完全匹配等)
您的第一个解决方案将提供所需的结果:
var requestedRules = dbContext.Rules
.Where(rule => lines.Contains(rule));
换句话说:从Rules
的集合中,仅选择那些Rules
值等于Raw
序列中的值之一的lines
。 / p>
我正在获得具有相同原始值的重复规则...)
很显然,您的源集合具有与原始值相同的规则!
如果只需要唯一的Raw值,则必须决定如何处理重复项:
Id Raw
1 "Hello"
2 "Hello"
您想要哪个?首先?最后?都?没有?有吗?
我们随心所欲:我们将以相同的原始值组成规则组,然后从每个组中获取第一个规则(或者,如果您想要最后一个,毕竟我们不在乎。但这有点效率较低。
var result = dbContext.Rules
.Where(rule => lines.Contains(rule))
.GroupBy(rule => rule.Raw)
.Select(group => group.FirstOrDefault());
换句话说:从Rules
的集合中,仅选择那些Rules
值等于Raw
序列中的值之一的lines
。从其余元素中组成具有相同原始值的规则组。然后从每个组中获取任何元素,例如第一个。
如果只想全部/只希望最后一个/只希望最后一个,那么您现在就知道该怎么办。