将 LINQ 用于此类查询的正确方法

本文关键字:方法 查询 LINQ 用于 | 更新日期: 2023-09-27 18:31:21

我最初使用的是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循环的需求(似乎我应该能够摆脱它),并通过不对列表的每个元素执行单独的数据库查询来使数据库查询更快。相反,我想对所有元素执行一个查询。谢谢!

将 LINQ 用于此类查询的正确方法

你可以通过这种方式更改你的代码,它会按预期转换为 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));
不幸的是,

修改后的版本将以与原始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));

你会得到

不支持异常:无法创建类型为"您的类型"的常量值。在此上下文中仅支持基元类型或枚举类型。

EF 执行单个数据库查询的唯一方法是手动生成一个 LINQ 查询,其中内存集合中的每个项目都有Concat,如下所示

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();

有关如何监视 EF 操作的信息,请参阅日志记录和截获数据库操作。

我会按如下方式使用它:

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"语句更容易理解和可读。