实体框架核心Linq其中NULL不起作用

时间:2016-12-09 17:08:43

标签: mysql entity-framework linq entity-framework-core

我正在使用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”。

2 个答案:

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