簡體   English   中英

Linq在多個(級聯)左連接上查詢NullReferenceException

[英]Linq query NullReferenceException on multiple (cascade) left joins

我正在使用Linq查詢來獲取客戶及其可選的主要地址(客戶可以擁有零個或多個地址)。 對象層次結構如下:

  • 顧客
    • CustomerAddress(包含布爾屬性Main)
      • 地址

這是我正在使用的查詢:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM