简体   繁体   English

从DbSet获取所有实体 <TEntity> 具有IEnumerable中的属性值

[英]Get all entities from DbSet<TEntity> with a property value that is in an IEnumerable

This seems like it would be really simple, but I cannot seem to resolve it. 这似乎很简单,但我似乎无法解决。 Using EF Core, I have a DbSet<Rule> Rules in my DbContext . 使用EF核心,我有一个DbSet<Rule> Rules在我DbContext

public class Rule
{
    public int Id { get; set; }
    public string Raw { get; set; }
}

I am trying to write a query where, given an IEnumerable<string> lines , give me all of the Rule s from the DbSet where its Raw value is an element in lines (exact match, not a substring of a value). 我试图写到哪,给定一个查询IEnumerable<string> lines ,给我所有的Rule ■从DbSet那里的Raw值中的一个元素lines (精确的匹配,值不是一个字符串)。

For some time, I was using something like: 一段时间以来,我一直在使用类似的东西:

private IQueryable<Rule> GetExistingRules() =>
    dbContext.Rules.Where(r => lines.Contains(r.Raw));

But, I have since discovered that (I think) this was not doing what I was expecting. 但是,从那以后,我发现(我认为)这没有达到我的期望。 (This method is immediately followed by inserting new Rule s for all elements of lines that do not currently exist. I was getting duplicate Rule s with the same Raw value...) I think, instead, I need to use .Intersect() ? (此方法之后立即为当前不存在的所有lines元素插入新的Rule 。我得到具有相同Raw值的重复Rule ……)我想,我需要使用.Intersect()

I tried using a custom EqualityComparer per this , but it throws an exception. 我尝试为此使用一个自定义的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;
        ...
    }

Could not parse expression 'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[FilterLists.Data.Entities.Rule]).Intersect(__p_0, __p_1)': This overload of the method 'System.Linq.Queryable.Intersect' is currently not supported. 无法解析表达式'value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1 [FilterLists.Data.Entities.Rule])。Intersect(__ p_0,__p_1)':方法'System.Linq.Queryable.Intersect的此重载目前不支持。

What is the best way to compose this query? 构成此查询的最佳方法是什么? Note that it is in a chain of DbContext interactions, so I'd prefer to keep the return type as an IQueryable to enable EF's lazy query composition. 请注意,它位于DbContext交互链中,因此我更喜欢将返回类型保留为IQueryable以启用EF的惰性查询组合。

Context on GitHub GitHub上的上下文

Update: More info on why I suspected the Contains() approach was not working: 更新:有关为什么我怀疑Contains()方法无效的更多信息:

This is the class where the query is being used. 这是使用查询的类。 I am seeing exceptions like below because the Raw column in the database has a unique constraint. 我看到如下异常,因为数据库中的Raw列具有唯一约束。 I thought my logic (use of Except() in CreateNewRules() ) would prevent any rows in Rules with duplicate Raw values, but maybe my issue lies elsewhere... 我想我的逻辑(使用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}));
}

Snippet from exception StackTrace (where '###Meebo:AdElement.Root' is a sample value for Raw in the Rules table): 异常StackTrace的摘要(其中“ ### Meebo:AdElement.Root”是“ Rules表中Raw的样本值):

FilterLists.Services.Snapshot.Snapshot.TrySaveAsync() in /home/travis/build/collinbarrett/FilterLists/src/FilterLists.Services/Snapshot/Snapshot.cs:line 43 Duplicate entry '###Meebo:AdElement.Root' for key 'IX_rules_Raw' at MySql.Data.MySqlClient.MySqlDataReader.ActivateResultSet(ResultSet resultSet) in C:\\projects\\mysqlconnector\\src\\MySqlConnector\\MySql.Data.MySqlClient\\MySqlDataReader.cs:line 93 at /home/travis/build/collinbarrett/FilterLists/src/FilterLists.Services/Snapshot/Snapshot.cs中的FilterLists.Services.Snapshot.Snapshot.TrySaveAsync():行43重复条目“ ### Meebo:AdElement.Root” C:\\ projects \\ mysqlconnector \\ src \\ MySqlConnector \\ MySql.Data.MySqlClient \\ MySqlDataReader.cs:第93行的MySql.Data.MySqlClient.MySqlDataReader.ActivateResultSet(ResultSet resultSet)中的键'IX_rules_Raw'

Update 2 : I am fairly sure the issue I was seeing with Contains() was due to this issue that has an active PR . 更新2 :我相当确定我在Contains()遇到的问题是由于此问题具有有效的PR Because my strings have all kinds of special characters, I think they were not getting escaped properly when passed into Contains() , but they seem to be properly escaped as parameters in a Join() . 因为我的字符串具有各种特殊字符,所以我认为它们在传递到Contains()无法正确转义,但似乎可以作为Join()参数正确转义。

Do not forget that when you use linQ with EFCore and IQueryable, it translate the c# code in Sql statements. 不要忘记,将linQ与EFCore和IQueryable一起使用时,它会转换Sql语句中的c#代码。

Did you tried this ? 你试过这个吗?

var query = from rule in dbContext.Rules
            join line in lines
                on rule.Raw equals line
            select rule;

You wrote: 你写了:

Give me all of the Rules from the DbSet where its Raw value is an element in lines (exact match etc.) 给我DbSet中的所有Rule,其中Raw值是行中的元素(完全匹配等)

Your first solution will give the desired result: 您的第一个解决方案将提供理想的结果:

var requestedRules = dbContext.Rules
   .Where(rule => lines.Contains(rule));

In words: from the collection of Rules , select only those Rules that have a Raw value that equals one of the values in the sequence of lines . 换句话说:从Rules集合中,仅选择那些Raw值等于lines序列中值之一的Rules

I was getting duplicate Rules with the same Raw value...) 我正在获得具有相同原始值的重复规则...)

Well apparently your source collection has Rules with the same Raw value! 显然,您的源集合具有与原始值相同的规则!

If you want only unique Raw values you'll have to decide what to do with duplicates: 如果只需要唯一的Raw值,则必须决定如何处理重复项:

Id  Raw
 1  "Hello"
 2  "Hello"

Which one do you want? 你想要哪一个? The first? 首先? the last? 最后? Both? 都? None? 没有? Any? 任何?

Let's go for Any: we'll make groups of Rules with same Raw value, and from every Group we take the first one (or if you want the last, after all we don't care. But that is a bit less efficient. 让我们去做任何事情:我们将原始值相同的规则组组成,然后从每个组中获取第一个规则(或者,如果您想要最后一个规则,毕竟我们不在乎。但这效率不高。

var result = dbContext.Rules
   .Where(rule => lines.Contains(rule))
   .GroupBy(rule => rule.Raw)
   .Select(group => group.FirstOrDefault());

In words: from the collection of Rules , select only those Rules that have a Raw value that equals one of the values in the sequence of lines . 换句话说:从Rules集合中,仅选择那些Raw值等于lines序列中值之一的Rules From the remaining elements make groups of Rules with same Raw value. 从其余元素中组成具有相同原始值的规则组。 Then from every Group take any element, for example the first one. 然后从每个组中选取任何元素,例如第一个元素。

If you want all / only the first / only the last, you'll know what to do by now. 如果您只想全部/只希望最后一个/只希望最后一个,那么您现在就知道该怎么办了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在实体框架中使用DbSet <TEntity> .Local属性 - Using DbSet<TEntity>.Local Property in Entity Framework 如何从 IEnumerable 获取属性值<object><div id="text_translate"><p>我有一个返回 IEnumerable 的方法</p><pre>public static IEnumerable&lt;object&gt; GetProps&lt;T&gt;(T obj) { var result = obj.GetType().GetProperties().Select(x =&gt; new { property = x.Name, value = x.GetValue(obj) }).Where(x =&gt; x.value == null).ToList(); return result; }</pre><p> 上面的代码将返回结果为[{"property":"YearOfBirth","value":null}]</p><p> 我现在尝试从返回的结果中获取属性值<strong>YearOfBirth</strong> 。 有人可以建议/帮助吗?</p></div></object> - How to get property value from IEnumerable<object> DbSet <TEntity> .Add(TEntity)和唯一性 - DbSet<TEntity>.Add(TEntity) and Uniqueness 从foreach中的dbSet获取值 - Get value from dbSet in foreach 数据库集 <TEntity> .Find()-反射 - DbSet<TEntity>.Find() - Reflection DbSet中的结果数<TEntity> - Number of results in DbSet<TEntity> 模拟DbSet <TEntity> 在存储库中 - Mocking DbSet<TEntity> in Repository 数据库集 <TEntity> ,可查询 <TEntity> -OOP概念 - DbSet<TEntity>, IQueryable<TEntity> - OOP concepts 如何从 DbContext 获取所有 DbSet - How to get all DbSet from DbContext 如何在web api中返回IEnumerable DbSet? - How to return IEnumerable DbSet in web api get?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM