[英]Why is Linq-to-sql incorrectly removing fields in my union?
我正在嘗試將多個查詢聚合在一起,為我的用戶提供最新的更新顯示,使用通用數據結構進行聯合支持。 在我的第一個函數中,我有以下select子句:
.Select(x => new PlayerUpdateInfo
{
FirstName = x.PodOptimizedSearch.FirstName,
LastName = x.PodOptimizedSearch.LastName,
RecruitId = x.RecruitId,
Date = x.Date,
UpdateMessage = x.IsAddedAction
? "Player was added to this recruiting board by " + x.Person.FirstName
+ " " + x.Person.LastName
: "Player was removed from this recruiting board by " +
x.Person.FirstName + " " + x.Person.LastName,
// Defaults for union support
Team = string.Empty,
UpdateComments = x.Comments,
TeamId = 0,
State = string.Empty
});
調用時,這會正確生成一個返回9個字段的查詢。 第二種方法的選擇是:
select new PlayerUpdateInfo
{
FirstName = recruit.FirstName,
LastName = recruit.LastName,
RecruitId = recruit.RecruitId,
Date = asset.CreateDate,
UpdateMessage = "New Full Game Added",
// Defaults for union support
Team = null,
UpdateComments = null,
TeamId = 0,
State = null
};
當此查詢本身運行時,它會正確返回9個值。 但是,當我嘗試做的時候
var query = GetFirstQuery();
query = query.union(GetSecondQuery());
我得到一個sql異常,查詢失敗,因為所有查詢都沒有相同數量的字段。 調查生成的SQL顯示第一個查詢是正確的,但Linq生成的第二個(聯合)查詢只有7個字段。 經過測試,似乎Linq似乎正在“優化”空值,因此它只返回一個空列而不是3,從而導致不匹配。
為什么Linq-to-sql錯誤地生成了聯合,我該如何解決這個問題呢?
好的問題似乎是針對.Net 3.5的Linq-to-Sql中的聯合鏈接。 這顯然不會發生在4中。 使用以下代碼:
protected IQueryable<PlayerUpdateInfo> Test1() { return PodDataContext.Assets .Select(x => new PlayerUpdateInfo { Date = DateTime.Now, FirstName = x.Title, LastName = string.Empty, RecruitId = 0, State = string.Empty, Team = string.Empty, TeamId = 0, UpdateComments = string.Empty, UpdateMessage = string.Empty }); } protected IQueryable<PlayerUpdateInfo> Test2() { return PodDataContext.SportPositions .Select(x => new PlayerUpdateInfo { Date = DateTime.Now, FirstName = string.Empty, LastName = x.Abbreviation, RecruitId = 0, State = string.Empty, Team = string.Empty, TeamId = 0, UpdateComments = string.Empty, UpdateMessage = string.Empty }); }
然后我通過聯合鏈: var q2 = Test1().Union(Test2()).Union(Test1());
在.Net 3.5中我得到以下sql,它有一個不匹配並失敗
SELECT [t4].[value] AS [RecruitId], [t4].[Title] AS [FirstName], [t4].[value2] AS [LastName], [t4].[value22] AS [Team], [t4].[value3] AS [Date] FROM ( SELECT [t2].[value], [t2].[Title], [t2].[value2], [t2].[value2] AS [value22], [t2].[value3] FROM ( SELECT @p0 AS [value], [t0].[Title], @p1 AS [value2], @p2 AS [value3] FROM [dbo].[Assets] AS [t0] UNION SELECT @p3 AS [value], @p4 AS [value2], [t1].[Abbreviation], @p5 AS [value3] FROM [dbo].[SportPositions] AS [t1] ) AS [t2] UNION SELECT @p6 AS [value], [t3].[Title], @p7 AS [value2], @p8 AS [value3] FROM [dbo].[Assets] AS [t3] ) AS [t4]
在.net 4中生成以下代碼:
SELECT [t4].[value] AS [RecruitId], [t4].[Title] AS [FirstName], [t4].[value2] AS [LastName], [t4].[value3] AS [Team], [t4].[value4] AS [TeamId], [t4].[value5] AS [State], [t4].[value6] AS [UpdateMessage], [t4].[value7] AS [UpdateComments], [t4].[value8] AS [Date] FROM ( SELECT [t2].[value], [t2].[Title], [t2].[value2], [t2].[value3], [t2].[value4], [t2].[value5], [t2].[value6], [t2].[value7], [t2].[value8] FROM ( SELECT @p0 AS [value], [t0].[Title], @p1 AS [value2], @p2 AS [value3], @p3 AS [value4], @p4 AS [value5], @p5 AS [value6], @p6 AS [value7], @p7 AS [value8] FROM [dbo].[Assets] AS [t0] UNION SELECT @p8 AS [value], @p9 AS [value2], [t1].[Abbreviation], @p10 AS [value3], @p11 AS [value4], @p12 AS [value5], @p13 AS [value6], @p14 AS [value7], @p15 AS [value8] FROM [dbo].[SportPositions] AS [t1] ) AS [t2] UNION SELECT @p16 AS [value], [t3].[Title], @p17 AS [value2], @p18 AS [value3], @p19 AS [value4], @p20 AS [value5], @p21 AS [value6], @p22 AS [value7], @p23 AS [value8] FROM [dbo].[Assets] AS [t3] ) AS [t4]
這是有效的SQL和工作。 由於各種原因我們無法將我們的生產系統帶到.net 4,有沒有人知道我可以在.net 3.5中解決這個問題的方法?
這是一個更新的答案 - 似乎在3.5中無法關閉優化。 我在LinqPad中嘗試了很多東西,只要有一個不是從列派生的C#表達式,查詢編譯器就會將它從SQL中刪除。
所以我們需要一些從列表達式派生的東西,但我們可以強制它總是為null
。
這是我能夠開始工作的一個解決方案 - 編寫一個條件表達式,當列值等於它自己時返回null
; 因此總是返回null
。 它不會很漂亮,因為3.5中的Linq to Sql似乎在優化查詢方面非常積極(我們必須對我們想要的每個null
使用唯一比較,例如在一個中使用==
,然后在下一個使用!=
上); 當我們修改它以處理空值時,如果你必須在可空列上執行此操作,它會變得更加丑陋:
我只是挑選了我可以從你的代碼中看到的列名 - 但你可能想要使用不同的:
select new PlayerUpdateInfo
{
FirstName = recruit.FirstName,
LastName = recruit.LastName,
RecruitId = recruit.RecruitId,
Date = asset.CreateDate,
UpdateMessage = "New Full Game Added",
// Defaults for union support
Team = recruit.FirstName = recruit.FirstName ? null : "",
UpdateComments = recruit.FirstName != recruit.FirstName ? "" : null,
TeamId = recruit.LastName = recruit.LastName ? null : "",
State = recruit.LastName != recruit.LastName ? "" : null
};
在LinqPad中,我在生成的SQL中為每個使用a的語句得到一個列表達式? :
? :
。 表達式另一側的""
是因為SQL抱怨CASE
語句有多個null。
很明顯 - 這個解決方案,除了像罪一樣丑陋之外,還有多少列可用於你進行這些假比較,實際上L2S映射了多少獨特的比較。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.