.NET Entity Framework - использование .Contains() для поиска значения байта в выражении Where
Я создаю IQueryable на основе параметров, которые я получаю от пользователя. Один из этих параметров - это множественный выбор, и мне нужно получить записи, содержащие любые из выбранных значений.
Код, который имеет дело с этим:
var ids = parameters.DeliveryID.ToArray();
courses = courses.Where(c => ids.Contains(c.CourseDeliveryID));
В приведенном выше коде:
1. ids - это массив байтов, и я должен убедиться, что он имеет несколько значений перед вызовом Contains().
2. c.CourseDeliveryID - значение байта.
В базе данных я храню CourseDeliveryID как tinyint (SQL Server 2008).
Компиляция просто прекрасна.
Когда я запускаю код, я получаю следующее исключение ArgumentException:
DbExpressionBinding requires an input expression with a collection ResultType.
Parameter name: input
Я нашел документацию для этого исключения:
http://technet.microsoft.com/en-us/library/system.data.common.commandtrees.expressionbuilder.dbexpressionbuilder.bindas.aspx
При попытке решить проблему я обнаружил, что если я использую тот же код в shorts, ints или longs, у меня нет проблем.
Я общаюсь с Microsoft об этом со вчерашнего дня и буду обновлять, когда узнаю больше, но тем временем я решил, что брошу его здесь, чтобы получить больше советов, если это возможно.
Спасибо заранее!
Ответы
Ответ 1
Я смог воспроизвести вашу ошибку в LINQPad и обнаружил, что использование List<byte>
вместо byte[]
будет работать:
// byte[] ids = new byte[] { 1, 64 }; <== causes ArgumentException
List<byte> ids = new List<byte> { 1, 64};
var c = Courses.Where (co => ids.Contains(co.CourseDeliveryId));
будет генерировать следующие sql и возвращать результаты:
SELECT
[Extent1].[CourseId] AS [CourseId],
[Extent1].[CourseName] AS [CourseName],
[Extent1].[CourseDeliveryId] AS [CourseDeliveryId]
FROM [dbo].[Courses] AS [Extent1]
WHERE [Extent1].[CourseDeliveryId] IN (1,64)
Интересно также, что использование int[]
или short[]
также будет работать, создавая этот sql:
SELECT
[Extent1].[CourseId] AS [CourseId],
[Extent1].[CourseName] AS [CourseName],
[Extent1].[CourseDeliveryId] AS [CourseDeliveryId]
FROM [dbo].[Courses] AS [Extent1]
WHERE (1 = CAST( [Extent1].[CourseDeliveryId] AS int)) OR (64 = CAST( [Extent1].[CourseDeliveryId] AS int))
но с помощью byte[]
вызывает исключение. Я могу только догадываться, что поставщик SQL Server EF пытается обработать byte[]
каким-то особым образом, в результате чего возникает это исключение.
Ответ 2
Хотя использование разных контейнеров решает проблему, вам не нужно менять тип контейнера.
Все, что вам нужно сделать, это назначить его IEnumerable:
IEnumerable<byte> ids = parameters.DeliveryID.ToArray();
courses = courses.Where(c => ids.Contains(c.CourseDeliveryID));
(В этом конкретном случае вы можете просто использовать ToList() вместо ToArray(), но в общем случае, если вы получите массив байтов и не хотите перестраивать его как список, который это сделает)