【问题标题】:Proper way to use LINQ for this type of query?将 LINQ 用于此类查询的正确方法?
【发布时间】:2016-01-21 04:07:09
【问题描述】:

我最初使用的是 foreach 循环,然后对于循环中的每个元素,我执行 LINQ 查询,如下所示:

foreach (MyObject identifier in identifiers.Where(i => i.IsMarkedForDeletion == false))
{
    if (this.MyEntities.Identifiers.Where(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3).Any())
    {
        return false;
    }
}

return true;

然后我这样修改:

if (identifiers.Any(i => !i.IsMarkedForDeletion && this.MyEntities.Identifiers.Where(pi => i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3).Any()))
{
   return false;
}

return true;

我的问题是这仍然是使用 LINQ 的错误方式吗?基本上,我想消除对 foreach 循环的需要(这似乎我应该能够摆脱它),并且还通过不对列表的每个元素执行单独的 DB 查询来使 DB 查询更快。相反,我想对所有元素执行一次查询。谢谢!

【问题讨论】:

  • identifiersMyEntitiesMyEntities.Identifiers类型是什么?它们是在内存集合还是数据库实体中?
  • 只有 MyEntities.Identifiers 在数据库中。其他一切都在记忆中。
  • 换句话说,identifiers 在内存集合中,MyEntities.Identifiers 是一个DbSet<Something>
  • 是的,没错。
  • 我会使用 foreach 循环,因为它更容易阅读。

标签: c# linq


【解决方案1】:

您可以通过这种方式更改您的代码,它会按预期转换为 SQL 语句。 为了防止转换过程中出现运行时错误,最好将 DBSet 保存到 IQueryable 变量中; identifiers 也应该是 IQueryable,所以你应该把你的代码改成这样(老实说,Resharper 在这个简短的 labda 中转换了你的 foreach):

IQueryable<MyObject2> identifiers = MyEntities.Identifiers.Where(i => i.IsMarkedForDeletion == false);
IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable();
return identifiers.All(identifier => !ids.Any(pi => identifier.Field1 == pi.Field1 && identifier.Field2 == pi.Field2 && identifier.Field3 == pi.Field3));

如果identifiers在内存集合中,你可以这样更改代码(希望字段是string):

IQueryable<MyObject2> ids = MyEntities.Identifiers.AsQueryable();
string[] values = identifiers.Where(i => i.IsMarkedForDeletion == false).Select(i => String.Concat(i.Field1, i.Field2, i.Field3)).ToArray();
return !ids.Any(i => values.Contains(i.Field1 + i.Field2 + i.Field3));

【讨论】:

  • 不。它将不会转换为SQL,因此相当于原来的foreach方法
  • 您能否提供信息,您为什么这么认为?因为,我有实践经验,而且效果很好
  • 因为刚刚检查过 - EF6。
  • 抱歉,错过了 identifiers 是一个内部集合。它也应该是 IQueryable。
  • 你为什么像我上面那样使用 All 而不是 Any?只是好奇。
【解决方案2】:

不幸的是,您修改后的版本将以与原始 foreach 方法完全相同的方式(即多个数据库查询)执行,因为 EF 不支持连接到内存集合的数据库查询(除了对于原始和枚举类型的集合),所以如果你尝试最合乎逻辑的方式

bool result = this.MyEntities.Identifiers.Any(pi => identifiers.Any(i =>
    !i.IsMarkedForDeletion &&
    i.Field1 == pi.Field1 && i.Field2 == pi.Field2 && i.Field3 == pi.Field3));

你会得到

NotSupportedException:无法创建类型为“YourType”的常量值。此上下文仅支持原始类型或枚举类型。

EF 执行单个数据库查询的唯一方法是手动构建一个带有 Concat 的 LINQ 查询,每个项目来自内存集合,就像这样

IQueryable<Identifier> query = null;
foreach (var item in identifiers.Where(i => !i.IsMarkedForDeletion))
{
    var i = item;
    var subquery = this.MyEntities.Identifiers.Where(pi => 
        pi.Field1 == i.Field1 && pi.Field2 == i.Field2 && pi.Field3 == i.Field3);
    query = query != null ? query.Concat(subquery) : subquery;
}
bool result = query != null && query.Any();

请参阅Logging and Intercepting Database Operations,了解如何监控 EF 操作。

【讨论】:

    【解决方案3】:

    我会这样使用它:

    if (identifiers.Where(i => !i.IsMarkedForDeletion &&     
    this.MyEntities.Identifiers.Field1 == i.Field1 &&
    this.MyEntities.Identifiers.Field2 == i.Field2 && 
    this.MyEntities.Identifiers.Field3 == i.Field3).Any()))
    {
       return false;
    }
    
    return true;
    

    我希望这会有所帮助。尽管需要输入更多内容,但它比使用多个 'where' 语句更易于理解和可读。

    【讨论】:

    • 我应该更具体一些。 MyEntities.Identifiers 是一组数据库对象,而不仅仅是一个对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 2019-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多