Как я могу запросить нулевые значения в структуре сущностей?
Я хочу выполнить такой запрос
var result = from entry in table where entry.something == null select entry;
и получить IS NULL
.
Отредактировано: после первых двух ответов я чувствую необходимость уточнить, что я использую Entity Framework, а не Linq to SQL. Метод object.Equals (), похоже, не работает в EF.
- дизайн схемы базы данных streamов сообщений
- Передайте строку соединения кодовому первому DbContext
- Entity framework 4.3 запускает миграцию при запуске приложения
- Как просмотреть SQL, созданный платформой Entity Framework?
- Организационно, где я должен ставить общие запросы при первом использовании кода Entity Framework?
Редактировать №2: Вышеупомянутый запрос работает по назначению. Он правильно генерирует IS NULL
. Однако мой производственный код
value = null; var result = from entry in table where entry.something == value select entry;
и сгенерированный SQL был something = @p; @p = NULL
something = @p; @p = NULL
. Кажется, что EF правильно переводит выражение константы, но если задействована переменная, она рассматривает это как обычное сравнение. На самом деле имеет смысл. Я закрою этот вопрос
- Как включить дочерний объект дочернего объекта в Entity Framework 5
- EF Code First "Недопустимое имя столбца« Дискриминатор », но без наследования
- Код структуры Entity Framework Сначала используйте Guid как идентификатор с другой колонкой Identity
- Как разогревать приложение ASP.NET MVC на IIS 7.5?
- Как передать параметры методу DbContext.Database.ExecuteSqlCommand?
- Установите десятичное значение (16, 3) для столбца в первом подходе кода в EF4.3
- Зависимость впрыска в единицу работы с использованием репозиториев
- Использование миграции Entity Framework (сначала кода) при производстве
Обходной путь для Linq-to-SQL:
var result = from entry in table where entry.something.Equals(value) select entry;
Обходной путь для Linq-to-Entities (ouch!):
var result = from entry in table where (value == null ? entry.something == null : entry.something == value) select entry;
Это неприятная ошибка, которая несколько раз укусила меня. Если эта ошибка также повлияла на вас, посетите отчет об ошибке в UserVoice и сообщите Microsoft, что эта ошибка также повлияла на вас.
Изменить: эта ошибка исправлена в EF 4.5 ! Спасибо всем за поддержку этой ошибки!
Для обратной совместимости, он будет включен – вам нужно вручную включить параметр, чтобы сделать entry == value
. Пока нет слов о том, что это за параметр. Будьте на связи!
Редактировать 2: Согласно этому сообщению командой EF, эта проблема была исправлена в EF6! Woohoo!
Мы изменили поведение EF6 по умолчанию для компенсации трехзначной логики.
Это означает, что существующий код, который опирается на старое поведение ( null != null
, но только при сравнении с переменной) , либо должен быть изменен, либо не будет полагаться на это поведение, либо установить UseCSharpNullComparisonBehavior
в false, чтобы использовать старое нарушение поведения.
Поскольку Entity Framework 5.0 вы можете использовать следующий код для решения вашей проблемы:
public abstract class YourContext : DbContext { public YourContext() { (this as IObjectContextAdapter).ObjectContext.ContextOptions.UseCSharpNullComparisonBehavior = true; } }
Это должно решить ваши проблемы, поскольку Entity Framerwork будет использовать сравнение «C # like».
Существует несколько упрощенное обходное решение, которое работает с LINQ to Entities:
var result = from entry in table where entry.something == value || (value == null && entry.something == null) select entry;
Это работает, потому что, как заметил А. А., особый случай LINQ to Entities x == null (т.е. сравнение равенства с нулевой константой) и переводит его в x IS NULL.
В настоящее время мы рассматриваем возможность изменения этого поведения, чтобы автоматически вводить компенсирующие сравнения, если обе стороны равенства являются нулевыми. Однако есть несколько проблем:
- Это может потенциально нарушить код, который уже зависит от существующего поведения.
- Новый перевод может повлиять на работу существующих запросов, даже если нулевой параметр используется редко.
В любом случае, будем ли мы работать над этим, будет сильно зависеть от относительного приоритета, который наши клиенты назначают ему. Если вы беспокоитесь об этой проблеме, я рекомендую вам проголосовать за нее на нашем новом сайте Feature Feature: https://data.uservoice.com .
Если это тип с нулевым значением, возможно, попробуйте использовать свойство HasValue?
var result = from entry in table where !entry.something.HasValue select entry;
У меня нет EF для тестирования здесь, хотя … просто предложение =)
var result = from entry in table where entry.something.Equals(null) select entry;
Ссылка MSDN : LINQ to SQL: .NET-интегрированный запрос для реляционных данных
для работы с Null Comparisons используйте Object.Equals()
вместо ==
проверьте эту ссылку
var result = from entry in table where entry.something == null select entry;
Вышеприведенный запрос работает по назначению. Он правильно генерирует IS NULL. Однако мой производственный код
var value = null; var result = from entry in table where entry.something == value select entry;
и сгенерированный SQL был что-то = @p; @p = NULL. Кажется, что EF правильно переводит выражение константы, но если задействована переменная, она рассматривает это как обычное сравнение. На самом деле имеет смысл.
Указывая, что все предложения Entity Framework <6.0 генерируют некоторый неудобный SQL. См. Второй пример для «чистого» исправления.
Смешное обходное решение
// comparing against this... Foo item = ... return DataModel.Foos.FirstOrDefault(o => o.ProductID == item.ProductID // ridiculous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948 && item.ProductStyleID.HasValue ? o.ProductStyleID == item.ProductStyleID : o.ProductStyleID == null && item.MountingID.HasValue ? o.MountingID == item.MountingID : o.MountingID == null && item.FrameID.HasValue ? o.FrameID == item.FrameID : o.FrameID == null && o.Width == w && o.Height == h );
приводит к SQL как:
SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName], [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductStyleID] AS [ProductStyleID], [Extent1].[MountingID] AS [MountingID], [Extent1].[Width] AS [Width], [Extent1].[Height] AS [Height], [Extent1].[FrameID] AS [FrameID], FROM [dbo].[Foos] AS [Extent1] WHERE (CASE WHEN (([Extent1].[ProductID] = 1 /* @p__linq__0 */) AND (NULL /* @p__linq__1 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[ProductStyleID] = NULL /* @p__linq__2 */) THEN cast(1 as bit) WHEN ([Extent1].[ProductStyleID] <> NULL /* @p__linq__2 */) THEN cast(0 as bit) END WHEN (([Extent1].[ProductStyleID] IS NULL) AND (2 /* @p__linq__3 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[MountingID] = 2 /* @p__linq__4 */) THEN cast(1 as bit) WHEN ([Extent1].[MountingID] <> 2 /* @p__linq__4 */) THEN cast(0 as bit) END WHEN (([Extent1].[MountingID] IS NULL) AND (NULL /* @p__linq__5 */ IS NOT NULL)) THEN CASE WHEN ([Extent1].[FrameID] = NULL /* @p__linq__6 */) THEN cast(1 as bit) WHEN ([Extent1].[FrameID] <> NULL /* @p__linq__6 */) THEN cast(0 as bit) END WHEN (([Extent1].[FrameID] IS NULL) AND ([Extent1].[Width] = 20 /* @p__linq__7 */) AND ([Extent1].[Height] = 16 /* @p__linq__8 */)) THEN cast(1 as bit) WHEN (NOT (([Extent1].[FrameID] IS NULL) AND ([Extent1].[Width] = 20 /* @p__linq__7 */) AND ([Extent1].[Height] = 16 /* @p__linq__8 */))) THEN cast(0 as bit) END) = 1
Необычайное обходное решение
Если вы хотите сгенерировать более чистый SQL, что-то вроде:
// outrageous < EF 4.5 nullable comparison workaround http://stackoverflow.com/a/2541042/1037948 Expression> filterProductStyle, filterMounting, filterFrame; if(item.ProductStyleID.HasValue) filterProductStyle = o => o.ProductStyleID == item.ProductStyleID; else filterProductStyle = o => o.ProductStyleID == null; if (item.MountingID.HasValue) filterMounting = o => o.MountingID == item.MountingID; else filterMounting = o => o.MountingID == null; if (item.FrameID.HasValue) filterFrame = o => o.FrameID == item.FrameID; else filterFrame = o => o.FrameID == null; return DataModel.Foos.Where(o => o.ProductID == item.ProductID && o.Width == w && o.Height == h ) // continue the outrageous workaround for proper sql .Where(filterProductStyle) .Where(filterMounting) .Where(filterFrame) .FirstOrDefault() ;
приводит к тому, что вы хотели в первую очередь:
SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName], [Extent1].[ProductID] AS [ProductID], [Extent1].[ProductStyleID] AS [ProductStyleID], [Extent1].[MountingID] AS [MountingID], [Extent1].[Width] AS [Width], [Extent1].[Height] AS [Height], [Extent1].[FrameID] AS [FrameID], FROM [dbo].[Foos] AS [Extent1] WHERE ([Extent1].[ProductID] = 1 /* @p__linq__0 */) AND ([Extent1].[Width] = 16 /* @p__linq__1 */) AND ([Extent1].[Height] = 20 /* @p__linq__2 */) AND ([Extent1].[ProductStyleID] IS NULL) AND ([Extent1].[MountingID] = 2 /* @p__linq__3 */) AND ([Extent1].[FrameID] IS NULL)
Похоже, что Linq2Sql также имеет эту «проблему». Похоже, что существует истинная причина такого поведения из-за того, включены ли ANSI NULL или OFF, но это пугает разум, почему прямое «== null» действительно работает так, как вы ожидали.
Лично я предпочитаю:
var result = from entry in table where (entry.something??0)==(value??0) select entry;
над
var result = from entry in table where (value == null ? entry.something == null : entry.something == value) select entry;
потому что он предотвращает повторение – хотя это не математически точно, но в большинстве случаев оно подходит.
Я не могу прокомментировать сообщение divega, но среди различных решений, представленных здесь, решение divega создает лучший SQL. И производительность мудрая и длина мудрый. Я только что проверил с помощью SQL Server Profiler и просмотрел план выполнения (с «SET STATISTICS PROFILE ON»).
К сожалению, в Entity Framework 5 DbContext проблема все еще не исправлена.
Я использовал это обходное решение (работает с MSSQL 2012, но параметр ANSI NULLS может быть устаревшим в любой будущей версии MSSQL).
public class Context : DbContext { public Context() : base("name=Context") { this.Database.Connection.StateChange += Connection_StateChange; } void Connection_StateChange(object sender, System.Data.StateChangeEventArgs e) { // Set ANSI_NULLS OFF when any connection is opened. This is needed because of a bug in Entity Framework // that is not fixed in EF 5 when using DbContext. if (e.CurrentState == System.Data.ConnectionState.Open) { var connection = (System.Data.Common.DbConnection)sender; using (var cmd = connection.CreateCommand()) { cmd.CommandText = "SET ANSI_NULLS OFF"; cmd.ExecuteNonQuery(); } } } }
Следует отметить, что это грязное решение, но оно может быть реализовано очень быстро и работает для всех запросов.
Если вы предпочитаете использовать синтаксис метода (lambda), как я, вы можете сделать то же самое:
var result = new TableName(); using(var db = new EFObjectContext) { var query = db.TableName; query = value1 == null ? query.Where(tbl => tbl.entry1 == null) : query.Where(tbl => tbl.entry1 == value1); query = value2 == null ? query.Where(tbl => tbl.entry2 == null) : query.Where(tbl => tbl.entry2 == value2); result = query .Select(tbl => tbl) .FirstOrDefault(); // Inspect the value of the trace variable below to see the sql generated by EF var trace = ((ObjectQuery) query).ToTraceString(); } return result;
var result = from entry in table where entry.something == value||entry.something == null select entry;
использовать это