[英]Linq query NullReferenceException on multiple (cascade) left joins
我正在使用Linq查詢來獲取客戶及其可選的主要地址(客戶可以擁有零個或多個地址)。 對象層次結構如下:
這是我正在使用的查詢:
var qry = from customer cus in GetDBContext(c).customer
join cusadd in GetDBContext(c).customeraddress on new { cus_code = cus.cus_code, main = "1" } equals new { cus_code = cusadd.cus_code, main = cusadd.Main_addr } into grpcusadd
from cusadd in grpcusadd.DefaultIfEmpty()
join add in GetDBContext(c).address on new { addr_code = cusadd.Addr_Code } equals new { addr_code = add.Addr_Code } into grpadd
from add in grpadd.DefaultIfEmpty()
select new { cus, cusadd, add };
var customers = qry.ToList();
當我在數據庫上執行它(通過EF)時,它會正確返回值。 當我在內存對象的模擬上下文中執行它時,我得到一個NullReferenceException:對象引用未設置為對象的實例。
我能夠通過在第二個左連接中檢查空值來修復此錯誤,因為第一個左連接返回空值:
join add in GetDBContext(c).address on new { addr_code = cusadd == null ? null : cusadd.Addr_Code } equals new { addr_code = add.Addr_Code } into grpadd
我找到了一個博文章,結論相同但沒有解釋: http : //technologycraftsmen.net/blog/2010/04/14/multiple-outer-joins-in-linq-to-sql/
為什么此查詢在本地對象上而不在數據庫上失敗?
應該級聯左外連接總是在Linq中這樣寫嗎?
感謝您的反饋意見!
Linq很精彩,但沒有抽象是完美的。 這是底層抽象漏掉一點的情況。
當表達式使用真實上下文執行時,它將使用LEFT JOIN轉換為Transact SQL語句。 表達式永遠不會在CLR中實際執行,一切都發生在數據庫中。 即使右表中沒有匹配的記錄,查詢也會成功。
在針對模擬上下文執行查詢時,實際的查詢執行發生在CLR中而不是數據庫中。 此時,表達式的運行就像編寫了非LINQ C#代碼一樣。 這意味着針對null的對象屬性的測試將拋出您看到的NullReferenceException。
想象如果這只是兩個序列之間的連接會發生什么會有所幫助:
var customers = new List<Customer> { new Customer { Id = 1, Name = "HasAddress" }, new Customer { Id = 2, Name = "HasNoAddress" } };
var addresses = new List<Address> { new Address { Id = 1, CustomerId = 1, Street = "123 Conselyea Street" } };
var customerAddresses = from Customer cus in customers
join address in addresses on cus.Id equals address.CustomerId into grouped
from ca in grouped.DefaultIfEmpty()
select new { CustomerId = cus.Id, Name = cus.Name, Street = ca.Street };
賦值“Street = ca.Street”將拋出NullReferenceException,因為我們的第二個客戶沒有匹配的地址。 我們如何解決這個問題? 使用三元運算符,就像您在級聯連接的修復程序中包含的一樣:
var customerAddresses = from Customer cus in customers
join address in addresses on cus.Id equals address.CustomerId into grouped
from ca in grouped.DefaultIfEmpty()
select new { CustomerId = cus.Id, Name = cus.Name, Street = (ca != null) ? ca.Street : null };
在您模擬上下文的情況下,您的級聯連接不是Transact SQL連接,它只是對象和序列的正常C#用法,如我的示例中所示。
我不知道編寫代碼的任何方法都不會忘記CLR中的執行與數據庫中的執行之間的規則差異。 通過為DefaultIfEmpty方法調用提供默認實例,可能會做一些棘手的事情,但這對我來說似乎很棘手。
希望一旦我們在C#中使用null傳播運算符,這將得到改進。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.