我正在使用Entity Framework Core和Linq编写查询以获取我的对象上的EndDate属性为NULL的所有条目。但是,EF不会将查询转换为正确的SQL,以使用非NULL的EndDate过滤掉对象。我正在使用这些软件包的MySQL数据库:
"MySql.Data.Core": "7.0.4-IR-191",
"MySql.Data.EntityFrameworkCore": "7.0.4-IR-191",
这是我的问题:
var employees = (from emp in _context.Employees.ToList()
join loc in _context.Locations.ToList()
on emp.HomeLocationId equals loc.LocationId
join rate in _context.EmployeeRates.ToList()
on emp.EmployeeId equals rate.EmployeeId
where rate.EndDate == null
select emp).ToList();
这是我的EndDate属性声明:
public DateTime? EndDate { get; set; }
生成的SQL根本不包含WHERE子句。我已经手动将此查询转换为MySQL,它运行正常:
SELECT e.FirstName, e.LastName, er.Rate, er.StartDate, er.EndDate
FROM qasdb.employees as e
JOIN qasdb.employeerates as er on er.EmployeeId = e.EmployeeId
JOIN qasdb.locations as l on l.LocationId = e.HomeLocationId
WHERE e.FirstName='Todd' AND er.EndDate is null
这是EF Core的问题吗?是否有一项已知的工作可以让Null比较起作用?
修改
这是生成的SQL。它似乎在做几个查询:
SELECT e.EmployeeRateId, e.EmployeeId, e.EndDate,
e.LastModifiedBy, e.Rate, e.StartDate FROM employeerates
AS e
SELECT emp.EmployeeId, emp.ActiveFlag, emp.City,
emp.CreateStamp, emp.Email, emp.EmergencyContactName,
emp.EmergencyContactPhone, emp.FirstName,
emp.HomeLocationId, emp.JobClass, emp.LastModifiedBy,
emp.LastName, emp.PhoneNumber, emp.Ssn, emp.State,
emp.Street, emp.UpdateStamp, emp.Zip FROM employees
AS emp
INNER JOIN locations AS loc ON emp.HomeLocationId = loc.LocationId
编辑#2
当我从每一行中删除ToList()调用时,SQL按预期生成:
SELECT emp.EmployeeId, emp.ActiveFlag, emp.City,
emp.CreateStamp, emp.Email, emp.EmergencyContactName,
emp.EmergencyContactPhone, emp.FirstName,
emp.HomeLocationId, emp.JobClass, emp.LastModifiedBy,
emp.LastName, emp.PhoneNumber, emp.Ssn, emp.State,
emp.Street, emp.UpdateStamp, emp.Zip FROM employees
AS emp
INNER JOIN locations AS loc ON emp.HomeLocationId = loc.LocationId
INNER JOIN employeerates AS rate ON emp.EmployeeId = rate.EmployeeId
WHERE rate.EndDate IS NULL
但是,当我删除.ToList()调用时,我将导航属性从Employee对象丢失到EmployeeRates列表。以下是我的Employee实体的设置方式:
public class Employee : BaseEntity
{
public int EmployeeId { get; set; }
......
public string JobClass { get; set; }
public int HomeLocationId { get; set; }
//Navigation Properties
public virtual Location HomeLocation { get; set; }
public virtual List<EmployeeRate> EmployeeRates { get; set; }
}
删除ToList()调用后,HomeLocation和EmployeeRates对象都返回“null”。
答案 0 :(得分:2)
您的第一个查询在每个DbSet之后包含ToList()
,它会将所有这些表中的所有数据都带到内存中。每当调用ToList()
时,EF都会执行查询。因此,即使您已经将整个linq查询一起编写,也有3个小型查询供EF处理(加载每个DbSet),因此您将3个不同的查询发送到数据库。 EF将从这些查询生成实体集合,其他所有内容都将在客户端进行评估,因此您不会看到where子句被转换为服务器,因为它根本不是提供给EF的查询的一部分。在此解决方案中,您可以看到加载的导航属性,因为所有数据都在内存中加载,然后EF将修复导航属性以填充它们。 (以便内存中的所有数据都处于一致状态。)
当您从所有DbSets中删除ToList()
个调用时,它将成为传递给EF的一个查询。 (最后ToList()
调用执行)因此EF将处理查询并转换where子句,但由于您只是预测Employee
个对象,因此EF只会获取它的属性,并且不会检索任何相关的数据。如果您希望填充导航属性,即加载相关数据,则必须使用Include
synatax明确告知EF。
您要查找的查询是
var result = (from e in db.Employees.Include(e => e.HomeLocation).Include(e => e.EmployeeRates)
join er in db.EmployeeRates on e.Id equals er.EmployeeId
where er.EndDate != null
select e).ToList();
对于要在最终结果中填充的每个导航,查询都有Include
。由于EF中仍不支持已过滤的包含(请参阅https://github.com/aspnet/EntityFramework/issues/1833),因此您无法直接在rate.EndDate
上指定where子句。因此,您需要手动加入该表并在EndDate
上应用where条件。由于HomeLocation
是一对一导航,因此EF将仅在主查询中检索相关数据(以填充导航)。 EmployeeRates
是集合导航,因此EF将发送单独的查询以加载相关员工的相关数据。重要的是,您无需手动加入HomeLocation
。包括将为您做到这一点。您需要手动加入EmployeeRate
仅用于过滤。
以下是在SQL
中生成的主要查询 SELECT [e].[Id], [e].[HomeLocationId], [l].[Id]
FROM [Employees] AS [e]
INNER JOIN [EmployeeRates] AS [er] ON [e].[Id] = [er].[EmployeeId]
INNER JOIN [Locations] AS [l] ON [e].[HomeLocationId] = [l].[Id]
WHERE [er].[EndDate] IS NOT NULL
ORDER BY [e].[Id]
答案 1 :(得分:0)
您使用的是Code First吗?
检查你的Map类是否没有这个
this.Property(t => t.EndDate).IsRequired();