Мой коллега получал ошибку с более сложным запросом с использованием LINQ to SQL в .NET 4.0, но, похоже, он легко воспроизводится в более простых условиях. Рассмотрим таблицу с именем TransferJob с синтетическим идентификатором и битовым полем.
Если мы сделаем следующий запрос
using (var ctx = DBDataContext.Create())
{
var withOutConstant = ctx.TransferJobs.Select(x => new { Id = x.TransferJobID, IsAuto = x.IsFromAutoRebalance });
var withConstant = ctx.TransferJobs.Select(x => new { Id = x.TransferJobID, IsAuto = true });//note we're putting a constant value in this one
var typeA = withOutConstant.GetType();
var typeB = withConstant.GetType();
bool same = typeA == typeB; //this is true!
var together = withOutConstant.Concat(withConstant);
var realized = together.ToList();//invalid cast exception
}
Недействительное исключение броска бросается там, где это указано. Но странно, у нас есть равенство типов при просмотре в отладчике.
Простое изменение второй до последней строки для перехода от IQueryable к использованию linq-to-objects
var together = withOutConstant.ToList().Concat(withConstant.ToList());
var realized = together.ToList();//no problem here
тогда все работает нормально, как ожидалось.
После некоторого первоначального копания, я вижу, что похоже, что программисты LINQ to SQL рассматривали производительность и на самом деле не имеют сгенерированного SQL pull неизменяемого значения в случае с явным значением true в версии withConstant.
Наконец, если я переключаю порядок, все работает:
var together = withConstant.Concat(withOutConstant); //no problem this way
Тем не менее, я все равно хотел бы узнать, если лучше детализировать то, что действительно происходит. Я считаю довольно странным, что они будут считаться равными типами, но вызывают недопустимое исключение литых. Что на самом деле происходит под обложками? Как я могу доказать это себе?
Трассировка стека:
at System.Data.SqlClient.SqlBuffer.get_Boolean()
at Read_<>f__AnonymousType2`2(ObjectMaterializer`1 )
at System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader`2.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at KBA.GenericTestRunner.Program.Main(String[] args) in c:\Users\nick\Source\Workspaces\KBA\Main\KBA\KBA.GenericTestRunner\Program.cs:line 59
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
Сгенерированный SQL следующий:
SELECT [t2].[TransferJobID] AS [Id], [t2].[IsFromAutoRebalance] AS [IsAuto]
FROM (
SELECT [t0].[TransferJobID], [t0].[IsFromAutoRebalance]
FROM [dbo].[TransferJob] AS [t0]
UNION ALL
SELECT [t1].[TransferJobID], @p0 AS [value]
FROM [dbo].[TransferJob] AS [t1]
) AS [t2]
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.34209
С отмененным порядком (который не сбой) SQL:
SELECT [t2].[TransferJobID] AS [Id], [t2].[value] AS [IsAuto]
FROM (
SELECT [t0].[TransferJobID], @p0 AS [value]
FROM [dbo].[TransferJob] AS [t0]
UNION ALL
SELECT [t1].[TransferJobID], [t1].[IsFromAutoRebalance]
FROM [dbo].[TransferJob] AS [t1]
) AS [t2]
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.34209
В мой предыдущий комментарий константа не тянется при выполнении
withConstant.ToList()
SELECT [t0].[TransferJobID] AS [Id]
FROM [dbo].[TransferJob] AS [t0]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 4.0.30319.34209