我正在尝试编写一个lambda查询来检索所有可供出租或即将推出的产品(结束日期为10天)。该查询将返回之前从未租借过的所有产品或者租用产品但很快就会推出的产品。
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Rent> Rents { get; set; }
}
public class Rent
{
public int Id { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public DateTime CreationTime { get; set; }
}
通常,SQL查询如下:
SELECT p.*, r1.*
FROM Products p
LEft JOIN Rents r1 ON (p.id = r1.ProductId)
LEFT OUTER JOIN Rents r2 ON (p.id = r2.ProductId AND
(r1.CreationTime < r2.CreationTime OR r1.CreationTime = r2.CreationTime AND r1.id < r2.id))
WHERE r2.id IS NULL AND (r1.EndDate is NULL OR r1.EndDate<='2018-06-24');
答案 0 :(得分:1)
通常你会使用这样的东西:
from p in db.Products
let r = p.Rents.OrderByDescending(r => r.CreationTime).FirstOrDefault()
where r == null || r.EndDate <= DateTime.Now.Date.AddDays(10)
select new { p, r }
不幸的是,即使在最近的EF Core 2.1中,这也导致了N + 1次查询。
因此,您可以使用LINQ等效的SQL查询。只是代替第二个反连接,你应该使用SQL NOT EXISTS
构造的LINQ等价物(即!Any
):
from p in db.Products
from r in p.Rents.DefaultIfEmpty()
where r == null || (r.EndDate <= DateTime.Now.Date.AddDays(10) &&
!p.Rents.Any(r2 => r.CreationTime < r2.CreationTime || (r.CreationTime == r2.CreationTime && r.Id < r2.Id)))
select new { p, r }
很好地转换为单个SQL查询。
答案 1 :(得分:0)
如果我正确理解您的问题:首先要包含导航属性:
var result = ctx.Products.Include(n => n.Rents)
查询将返回之前从未租借的所有产品或
然后检查Rents
为空的位置
result.Where(product => product.Rents == null ...
或者出租产品但很快就会上市
result.Where(product => product.Rents == null || product.Rents.Max(rent => rent.EndDate) <= DateTime.Now.AddDays(10))