2010-03-14 55 views
2

比方說,我有一個Order表,它有一個FirstSalesPersonId字段和一個SecondSalesPersonId字段。這兩個都是引用SalesPerson表的外鍵。對於任何給定的訂單,可以將一個或兩個銷售人員記入訂單。換句話說,FirstSalesPersonId永遠不可能是NULL,但SecondSalesPersonId可以是NULL我可以強制自動生成的Linq-to-SQL類使用OUTER JOIN嗎?

當我將我的OrderSalesPerson表到「LINQ to SQL類」設計圖面,該類建設者斑點從Order表兩個FK關係到SalesPerson表,並因此產生Order類有一個SalesPerson場和一個SalesPerson1字段(爲了避免混淆,我可以將其重命名爲SalesPerson1SalesPerson2)。

因爲我總是希望有營業員可用數據,每當我處理訂單,我使用DataLoadOptions.LoadWith指定兩個營業員字段填充填充了單實例時,如下所示:

dataLoadOptions.LoadWith<Order>(o => o.SalesPerson1); 
dataLoadOptions.LoadWith<Order>(o => o.SalesPerson2); 

我遇到的問題是,LINQ到SQL是使用類似下面的SQL來加載順序:

SELECT ... 
FROM Order O 
INNER JOIN SalesPerson SP1 ON SP1.salesPersonId = O.firstSalesPersonId 
INNER JOIN SalesPerson SP2 ON SP2.salesPersonId = O.secondSalesPersonId 

這將使意義,如果總有兩個銷售人員記錄,但由於有時沒有第二個銷售人員(secondSalesPersonIdNULL),因此INNER JOIN會導致查詢在該情況下不返回任何記錄。

我在這裏有效地想要將第二個INNER JOIN更改爲LEFT OUTER JOIN。有沒有辦法通過用於類生成器的用戶界面來執行此操作?如果不是,我還能如何實現這一目標? (請注意,因爲我幾乎全部使用生成的類,所以如果我可以避免的話,我寧願沒有在這一種情況下加上一些東西)。


編輯:按我的評論回覆中,SecondSalesPersonId可空(在數據庫中,並在生成的類)。

回答

1

的默認行爲實際上是一個LEFT JOIN,假設你已經正確地建立模型。

這裏的,我只是在我自己的數據庫的一個測試一個稍微匿名例如:

class Program 
{ 
    static void Main(string[] args) 
    { 
     using (TestDataContext context = new TestDataContext()) 
     { 
      DataLoadOptions dlo = new DataLoadOptions(); 
      dlo.LoadWith<Place>(p => p.Address); 
      context.LoadOptions = dlo; 

      var places = context.Places.Where(p => p.ID >= 100 && p.ID <= 200); 
      foreach (var place in places) 
      { 
       Console.WriteLine(p.ID, p.AddressID); 
      } 
     } 
    } 
} 

這僅僅是一個簡單的測試,打印出的地方,他們的地址ID的列表。這是出現在分析器查詢文本:

SELECT [t0].[ID], [t0].[Name], [t0].[AddressID], ... 
FROM [dbo].[Places] AS [t0] 
LEFT OUTER JOIN (
    SELECT 1 AS [test], [t1].[AddressID], 
     [t1].[StreetLine1], [t1].[StreetLine2], 
     [t1].[City], [t1].[Region], [t1].[Country], [t1].[PostalCode] 
    FROM [dbo].[Addresses] AS [t1] 
) AS [t2] ON [t2].[AddressID] = [t0].[AddressID] 
WHERE ([t0].[PlaceID] >= @p0) AND ([t0].[PlaceID] <= @p1) 

這不正是一個非常漂亮的查詢(你猜我的一樣好,什麼是1 as [test]是一回事),但它是明確一個LEFT JOIN並沒有表現出你似乎遇到的問題。這只是使用生成的類,我沒有做任何改變。

請注意,我也測試了這種雙重關係(即單個Place有兩個Address引用,一個是空的,一個不是),我得到完全相同的結果。第一個(不可爲空)變成了INNER JOIN,第二個變成了LEFT JOIN

它必須是你的模型中的東西,比如改變第二個參考的可空性。我知道你說它配置爲空,但也許你需要仔細檢查?如果它肯定是可以空的,那麼我建議你發佈你的完整模式和DBML,這樣有人可以嘗試重現你所看到的行爲。

0

如果您使數據庫表中的secondSalesPersonId字段爲空,LINQ-to-SQL應正確構造Association對象,以便生成的SQL語句將執行LEFT OUTER JOIN

UPDATE: 由於該字段爲空,您的問題可能是在明確聲明dataLoadOptions.LoadWith<>()。我在當前有一個訂單的項目中運行的情況類似,但訂單經歷了多個階段。每個階段都對應一個單獨的表,其中包含與該階段相關的數據。我只是簡單地檢索訂單,如果存在的話,相應的數據也隨之而來。我根本不使用dataLoadOptions,而是按照我需要的做。例如,如果訂單具有采購訂單記錄但沒有發票記錄,則Order.PurchaseOrder將包含採購訂單數據,並且Order.Invoice將爲空。我的查詢看起來是這樣的:

DC.Orders.Where(a => a.Order_ID == id).SingleOrDefault(); 

我儘量不要微觀LINQ到SQL ......它的什麼,我需要開箱的95%。

更新2: 我發現this post,討論,以便在使用DefaultIfEmpty()以NULL填充子實體如果他們不存在。我LINQPad試過了,在我的數據庫,並完成轉換的例子lambda語法(因爲這是我用的):

ParentTable.GroupJoin 
(
    ChildTable, 
    p => p.ParentTable_ID, 
    c => c.ChildTable_ID, 
    (p, aggregate) => new { p = p, aggregate = aggregate } 
) 
.SelectMany (a => a.aggregate.DefaultIfEmpty(), 
    (a, c) => new 
    { 
     ParentTableEntity = a.p, 
     ChildTableEntity = c 
    } 
) 

從我從這句話弄明白,該羣組加入表達涉及父和子表,而SelectMany表達式聚合相關的子記錄。關鍵似乎是使用DefaultIfEmpty,即使沒有相關的子記錄,也會強制包含父實體記錄。 (感謝引人注目我進一步挖成這樣...我想我可能已經找到了一些有用的東西,以幫助一個相當龐大的報告我已經在我的管線了...)

更新3: 如果目標是保持簡單,那麼看起來您必須直接在Select()表達式中引用這些銷售人員字段。首先使用LoadWith <>()的原因是因爲這些表沒有在查詢語句中的任何位置被引用,所以LINQ引擎不會自動將該信息拉入。

作爲例如,給出這樣的結構:

MailingList    ListCompany 
===========    =========== 
List_ID (PK)    ListCompany_ID (PK) 
ListCompany_ID (FK)  FullName (string) 

我想與特定的郵件列表相關的公司的名稱:

MailingLists.Where(a => a.List_ID == 2).Select(a => a.ListCompany.FullName) 

如果該協會尚未作出,這意味着ListCompany_ID FIEL d在MailingList表中該記錄等於null,這是由LINQ引擎生成的結果SQL:

SELECT [t1].[FullName] 
FROM [MailingLists] AS [t0] 
LEFT OUTER JOIN [ListCompanies] AS [t1] ON [t1].[ListCompany_ID] = [t0].[ListCompany_ID] 
WHERE [t0].[List_ID] = @p0 
+0

@Neil:它*爲*可空(甚至在「鏈接到SQL類」UI中顯示爲空)。 – 2010-03-14 22:12:30

+0

@尼爾:是的,如果我不使用LoadWith,那麼它只會按需加載銷售人員數據。但是,在我的情況下,我將在一個循環中處理數百個訂單記錄,這將意味着數百次訪問數據庫以單獨獲取每個銷售人員記錄。這正是我試圖用LoadWith避免的。它與第一個銷售人員記錄一起工作;我試圖讓它與第二個一起工作。 – 2010-03-15 09:28:21

+0

@Neil:謝謝,我非常感謝你的時間,但你已經有點離開了我想要去的地方,那就是當我實例化一個Order對象時,Order.SalesPerson1和Order.SalesPerson2字段在相同的數據庫查詢中同時填充。我不想編寫SQL來執行它(或LINQ僞SQL),我只是希望它能像「簡單」情況下那樣工作。這似乎並不合理,因爲這不是一個複雜的情況。 – 2010-03-18 11:32:07