【发布时间】:2019-01-26 14:04:58
【问题描述】:
这似乎很简单,但我似乎无法解决它。使用 EF Core,我的 DbContext 中有一个 DbSet<Rule> Rules。
public class Rule
{
public int Id { get; set; }
public string Raw { get; set; }
}
我正在尝试编写一个查询,在给定IEnumerable<string> lines 的情况下,给我来自DbSet 的所有Rules,其中它的Raw 值是lines 中的一个元素(完全匹配,不是值的子字符串)。
有一段时间,我在使用类似的东西:
private IQueryable<Rule> GetExistingRules() =>
dbContext.Rules.Where(r => lines.Contains(r.Raw));
但是,我后来发现(我认为)这并没有达到我的预期。 (此方法之后立即为当前不存在的lines 的所有元素插入新的Rules。我得到了具有相同Raw 值的重复Rules...)我认为,相反,我需要使用.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 列具有唯一约束。我认为我的逻辑(在CreateNewRules() 中使用Except())会阻止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' 是 Rules 表中 Raw 的示例值):
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 结果集)在 C:\projects\mysqlconnector\src\MySqlConnector\MySql.Data.MySqlClient\MySqlDataReader.cs:line 93 在
更新 2:我很确定我看到的 Contains() 问题是由于 this issue 具有 active PR。因为我的字符串有各种特殊字符,我认为它们在传递给Contains() 时没有被正确转义,但它们似乎作为Join() 中的参数被正确转义。
【问题讨论】:
-
你能再解释一下
Contains方法有什么问题吗(因为它似乎是从 db 获取现有规则的正确方法)? -
@IvanStoev 请参阅问题底部的更新。可能是我问了一个完全错误的问题……我不确定。
-
异常似乎是由不同的代码 (
Snapshot.TrySaveAsync) 生成的。问题很可能是由snapEntity.AddedSnapshotRules.Add[Range]引起的 - 如果snapEntity附加到不同的上下文,则该上下文的更改跟踪器可能会认为Rule对象是新的。 -
SnapshotBatch使用与调用者Snapshot.TrySaveAsync()相同的DbContext实例。但是,由于Snapshot启动了多个SnapshotBatch实例,我想知道是否由于一切都是异步的而存在冲突...... -
请注意,数据库中的比较可能不区分大小写。因此,如果在数据库中有规则
A和lines包含a和A(或仅a),您将获得现有规则:A,除A之外的行是a,因为 C# 区分大小写,您将尝试插入a,这是数据库的重复项。
标签: c# mysql entity-framework linq entity-framework-core