Lambda 表达式 LINQ 等效于 SQL 在同一表/变量上存在查询
Posted
技术标签:
【中文标题】Lambda 表达式 LINQ 等效于 SQL 在同一表/变量上存在查询【英文标题】:Lambda Expression LINQ Equivalent to SQL Exists Query on Same Table/Variable 【发布时间】:2018-11-25 03:51:16 【问题描述】:我正在尝试使用 lambda 表达式格式在 LINQ 中复制以下 SQL 查询(以使其与迄今为止开发的代码保持一致):
SELECT *
FROM Product p
WHERE p.DateObsolete IS NULL
OR p.DateObsolete > GETDATE()
OR EXISTS (
SELECT NULL
FROM dbo.Product p1
WHERE p1.Ref01 = p.Ref01
AND p1.Ref02 = p.Ref02
AND p1.Ref03 = p.Ref03
AND p1.Version = p.Version + 1
AND p1.DateApproved IS NULL
)
查看了其他问题(Linq subquery same table using lambda 是我能找到的最接近的问题,但没有显示如何“或”条件)在 SO 和其他地方我认为以下方法可行,但它只会导致堆栈溢出(严重) 异常和一条关于未加载 pdb 文件的消息,我认为这有点牵强。
products = products
.Where(p => !p.DateObsolete.HasValue
|| p.DateObsolete > DateTime.Now
|| products.Any(p1 => p1.Ref01 == p.Ref01
&& p1.Ref02 == p.Ref02
&& p1.Ref03 == p.Ref03
&& p1.Version == p.Version + 1
&& p1.DateApproved == null));
products 是一个 IQueryable 变量。
产品在此上下文中定义为 DbSet:
public class Product
public int ProductID get; set;
[MaxLength(200)]
public string Name get; set;
[MaxLength(3)]
public string Ref01 get; set;
[MaxLength(3)]
public string Ref02 get; set;
public int Ref03 get; set;
public int Version get; set;
public DateTime? DateReceivedByGraphics get; set;
public DateTime? DateApproved get; set;
public DateTime? DateObsolete get; set;
public bool Deleted get; set;
public bool Discontinued get; set;
如您所知,我是 LINQ 的新手(以及在 SO 上发布问题),因此我们将不胜感激地收到任何帮助。
【问题讨论】:
我们能看到 Product 实体吗?我的意思是这个查询中隐含的所有属性。 缺少信息:EF 版本 +products
的类型和来源。我猜数据库是 Sql Server。
尝试将您的表达式分配给一个新变量,例如var newProducts = products.Where(.....
很可能与products
变量闭包有关。尝试var query = products.Where(...)
,而不是将结果查询分配给products
变量。
对不起,@AleksAndreev 和 Ivan,我再次尝试重新分配(我试图追踪失败的确切点)并且它起作用了:我不确定我做了什么不同的事情。
【参考方案1】:
您可以通过加入来完成此操作。
DateTime date = DateTime.Today; // or .Now
var products = context.Products.Join(context.Products,
p1 => new p1.Ref01, p1.Ref02, p1.Ref03 ,
p2 => new p2.Ref01, p2.Ref02, p2.Ref03 ,
(p1, p2) => new Product = p1, p1.Version, JoinedVersion = p2.Version, JoinedDateApproved = p2.DateApproved )
.Where(x=> x.Product.DateObsolete > date && x.JoinedVersion == x.Version+1 && !x.JoinedDateApproved.HasValue)
.Select(x=>x.Product)
.ToList();
这会在 Ref 1-3 上将 Product 与自身连接,然后选择“左侧”项目及其版本、“右侧”版本和批准日期。 Where 条件隔离了“右”版本比左版本大 1 并且没有批准日期的情况。结果将是具有符合这些条件的对应项的“剩余”产品。
更新: 如果您已经将产品过滤到一组已知的适用产品,那么这将对对象起作用。例如:
// given products is an IQueryable representing the filtered products...
DateTime date = DateTime.Today; // or .Now
var productList = products.ToList(); // Materialize the EF Queryable into list of entities.
productList = productList.Join(productList,
p1 => new p1.Ref01, p1.Ref02, p1.Ref03 ,
p2 => new p2.Ref01, p2.Ref02, p2.Ref03 ,
(p1, p2) => new Product = p1, p1.Version, JoinedVersion = p2.Version, JoinedDateApproved = p2.DateApproved )
.Where(x=> x.Product.DateObsolete > date && x.JoinedVersion == x.Version+1 && !x.JoinedDateApproved.HasValue)
.Select(x=>x.Product)
.ToList();
如果您的目标是尝试将其保留为范围为 EF 的 IQueryable,那么我怀疑如果可能的话,它可能不值得复杂性/时间。最坏的情况是,如果您确实想要保留 IQueryable,请使用上述方法将产品 ID 选择到列表中,然后将该列表用作 IQueryable 的过滤器。
var productList = products.ToList(); // Materialize the EF Queryable into list of entities.
// Fetch a list of applicable product IDs.
var productIds = productList.Join(productList,
p1 => new p1.Ref01, p1.Ref02, p1.Ref03 ,
p2 => new p2.Ref01, p2.Ref02, p2.Ref03 ,
(p1, p2) => new ProductId = p1.ProductId, DateObsolete = p1.DateObsolete, p1.Version, JoinedVersion = p2.Version, JoinedDateApproved = p2.DateApproved )
.Where(x=> x.DateObsolete > date && x.JoinedVersion == x.Version+1 && !x.JoinedDateApproved.HasValue)
.Select(x=>x.ProductId)
.ToList();
// Filter the original IQueryable.
products = products.Where(x => productIds.Contains(x.ProductId));
【讨论】:
谢谢,但我不确定我能不能这样做。更多背景知识:此查询是在原始产品集合上运行的几个查询之一,用于识别满足用户指定搜索条件的产品集合,因此我需要针对产品变量中的集合而不是原始数据集运行它。我试过了,似乎无法从变量开始加入。抱歉,这只是暴露了我对 LINQ 缺乏了解!【参考方案2】:正如 Aleks Andreev 和 Ivan Stoev 所建议的,将表达式分配给一个新变量可以解决问题。我不确定为什么这第一次不起作用,但我的猜测是,在完成查询后,我尝试将结果重新分配回原始变量 - 为了不必完全更改变量名称我更改后的代码。
【讨论】:
以上是关于Lambda 表达式 LINQ 等效于 SQL 在同一表/变量上存在查询的主要内容,如果未能解决你的问题,请参考以下文章
如何在 LINQ 和 Lambda 表达式 LINQ C# 上正确执行 SQL 查询
帮助我使用实体框架从 SQL 转换为 linq 嵌套 lambda 表达式
如何将带有内连接的 sql 查询转换为 linq lambda 表达式?