使用IEnumerable中的属性值从DbSet <tentity>获取所有实体

时间:2018-08-20 12:29:27

标签: c# mysql entity-framework linq entity-framework-core

这似乎非常简单,但我似乎无法解决。使用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的惰性查询组合。

Context on 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的样本值):

  

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()中的参数正确转义。

2 个答案:

答案 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。从其余元素中组成具有相同原始值的规则组。然后从每个组中获取任何元素,例如第一个。

如果只想全部/只希望最后一个/只希望最后一个,那么您现在就知道该怎么办。