这似乎很简单,但我似乎无法解决它。使用ef核心,我有一个 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()
?
我试着使用一个自定义的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;
...
}
无法分析表达式“value(microsoft.entityframeworkcore.query.internal.entityqueryable1[filterlists.data.entities.rule]).intersect(\u p\u 0,\u p\u 1)”:当前不支持方法“system.linq.queryable.intersect”的此重载。 撰写此查询的最佳方式是什么?请注意,它是在一个
DbContext交互,所以我更喜欢将返回类型作为
IQueryable启用ef的延迟查询组合。 github上的上下文 更新:更多关于我为什么怀疑
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
表):
/home/travis/build/collinbarrett/filterlists/src/filterlists.services/snapshot/snapshot.cs中的filterlists.services.snapshot.snapshot.trysavesync():第43行重复条目'###meebo:adelement.root'对于mysql.data.mysqlclient.mysqldatareader.activateresultset(resultset resultset)中的键'ix\u rules\u raw'c:\projects\mysqlconnector\src\mysqlconnector\mysql.data.mysqlclient\mysqldatareader.cs:第93行
更新2:我很肯定我看到的问题 Contains()
是因为这个问题,有一个活动的公关。因为我的字符串有各种特殊字符,我认为他们没有得到正确转义时,传入 Contains()
,但它们似乎在 Join()
.
2条答案
按热度按时间xeufq47z1#
不要忘记,当您将linq与efcore和iqueryable一起使用时,它会在sql语句中转换c代码。
你试过这个吗?
shyt4zoc2#
你写道:
给出dbset的所有规则,其中原始值是行中的元素(精确匹配等)
您的第一个解决方案将提供所需的结果:
大写:来自
Rules
,仅选择那些Rules
有一个Raw
值,该值等于lines
.我得到了相同原始值的重复规则…)
很显然,你的源代码集合有相同原始值的规则!
如果只需要唯一的原始值,则必须决定如何处理副本:
你想要哪一个?第一个?最后?两者都有?没有?有吗?
让我们选择任何一个:我们将用相同的原始值创建一组规则,然后从每个组中选择第一个(或者如果您想要最后一个,毕竟我们不在乎)。但这样的效率要低一点。
大写:来自
Rules
,仅选择那些Rules
有一个Raw
值,该值等于lines
. 从其余的元素中生成具有相同原始值的规则组。然后从每个组中选取任何元素,例如第一个元素。如果你想要所有的/只有第一个/只有最后一个,你现在就知道该怎么办了。