實體框架核心Linq其中NULL不起作用

entity-framework entity-framework-core linq mysql

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

一般承認的答案

您的第一個查詢在每個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]

熱門答案

你在使用Code First嗎?

檢查你的Map類中是否沒有這個

this.Property(t => t.EndDate).IsRequired();



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow