Поделиться через


Известные проблемы и рекомендации в LINQ to Entities

В этом разделе содержатся сведения о известных проблемах с запросами LINQ to Entity.

Запросы LINQ, которые нельзя кэшировать

Начиная с .NET Framework 4.5 запросы LINQ to Entity автоматически кэшируются. Однако запросы LINQ to Entity, которые применяют оператор Enumerable.Contains к коллекциям в памяти, не кэшируются автоматически. Кроме того, параметризация коллекций в памяти в скомпилированных запросах LINQ запрещена.

Упорядочение сведений, потерянных

Проецирование столбцов в анонимный тип приведет к потере сведений о заказе в некоторых запросах, выполняемых в базе данных SQL Server 2005, на уровне совместимости 80. Это происходит, когда имя столбца в списке order-by соответствует имени столбца в селекторе, как показано в следующем примере:

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    // Ordering information is lost when executed against a SQL Server 2005
    // database running with a compatibility level of "80".
    var results = context.Contacts.SelectMany(c => c.SalesOrderHeaders)
        .OrderBy(c => c.SalesOrderDetails.Count)
        .Select(c => new { c.SalesOrderDetails.Count });

    foreach (var result in results)
        Console.WriteLine(result.Count);
}
Using context As New AdventureWorksEntities()
    ' Ordering information is lost when executed against a SQL Server 2005
    ' database running with a compatibility level of "80".
    Dim results = context.Contacts.SelectMany(Function(c) c.SalesOrderHeaders) _
        .OrderBy(Function(c) c.SalesOrderDetails.Count) _
        .Select(Function(c) New With {c.SalesOrderDetails.Count})

    For Each result In results
        Console.WriteLine(result.Count)
    Next
End Using

Неподписанные целые числа не поддерживаются

Указание типа целого числа без знака в запросе LINQ to Entity не поддерживается, так как Entity Framework не поддерживает целые числа без знака. Если указать целое число без знака, во время перевода выражений запроса будет возникать исключение ArgumentException, как показано в следующем примере. В этом примере запрашивается заказ с идентификатором 48000.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    uint s = UInt32.Parse("48000");

    IQueryable<SalesOrderDetail> query = from sale in context.SalesOrderDetails
                                         where sale.SalesOrderID == s
                                         select sale;

    // NotSupportedException exception is thrown here.
    try
    {
        foreach (SalesOrderDetail order in query)
            Console.WriteLine("SalesOrderID: " + order.SalesOrderID);
    }
    catch (NotSupportedException ex)
    {
        Console.WriteLine($"Exception: {ex.Message}");
    }
}
Using context As New AdventureWorksEntities()
    Dim saleId As UInteger = UInt32.Parse("48000")

    Dim query = _
        From sale In context.SalesOrderDetails _
        Where sale.SalesOrderID = saleId _
        Select sale

    Try
        ' NotSupportedException exception is thrown here.
        For Each order As SalesOrderDetail In query
            Console.WriteLine("SalesOrderID: " & order.SalesOrderID)
        Next
    Catch ex As NotSupportedException
        Console.WriteLine("Exception: " + ex.Message)
    End Try
End Using

Ошибки преобразования типов

В Visual Basic, когда свойство сопоставляется со столбцом битового типа SQL Server со значением 1 с помощью функции CByte, возникает ошибка SqlException с сообщением "Ошибка арифметического переполнения". В следующем примере выполняется запрос столбца Product.MakeFlag в примере базы данных AdventureWorks, а исключение создается при итерации результатов запроса.

Using context As New AdventureWorksEntities()
    Dim productsList = _
        From product In context.Products _
        Select CByte(product.MakeFlag)

    ' Throws an SqlException exception with a "Arithmetic overflow error 
    ' for data type tinyint" message when a value of 1 is iterated over.
    For Each makeFlag In productsList
        Console.WriteLine(makeFlag)
    Next
End Using

Ссылка на не скалярные переменные не поддерживается

Ссылка на не скалярные переменные, например сущность, в запросе не поддерживается. При выполнении такого запроса создается исключение NotSupportedException с сообщением "Не удалось создать константное значение типа EntityType. В этом контексте поддерживаются только примитивные типы (например, Int32, String и GUID).

Примечание.

Ссылка на коллекцию скалярных переменных поддерживается.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    Contact contact = context.Contacts.FirstOrDefault();

    // Referencing a non-scalar closure in a query will
    // throw an exception when the query is executed.
    IQueryable<string> contacts = from c in context.Contacts
        where c == contact
        select c.LastName;

    try
    {
        foreach (string name in contacts)
        {
            Console.WriteLine($"Name: ");
        }
    }
    catch (NotSupportedException ex)
    {
        Console.WriteLine(ex.Message);
    }
}
Using context As New AdventureWorksEntities()

    Dim contact As Contact = context.Contacts.FirstOrDefault()

    ' Referencing a non-scalar closure in a query will
    ' throw an exception when the query is executed.
    Dim contacts = From c In context.Contacts _
                   Where c.Equals(contact) _
                   Select c.LastName

    Try
        For Each name As String In contacts
            Console.WriteLine("Name: ", name)
        Next

    Catch ex As Exception
        Console.WriteLine(ex.Message)
    End Try

End Using

Вложенные запросы могут завершиться ошибкой с SQL Server 2000

При использовании SQL Server 2000 запросы LINQ to Entity могут завершиться ошибкой, если они создают вложенные Transact-SQL запросы, которые являются тремя или более уровнями.

Проецирование на анонимный тип

Если определить начальный путь запроса для включения связанных объектов с помощью метода Include в ObjectQuery<T>, а затем использовать LINQ для проецирования возвращаемых объектов в анонимный тип, объекты, указанные в методе включения, не включаются в результаты запроса.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    var resultWithoutRelatedObjects =
        context.Contacts.Include("SalesOrderHeaders").Select(c => new { c }).FirstOrDefault();
    if (resultWithoutRelatedObjects.c.SalesOrderHeaders.Count == 0)
    {
        Console.WriteLine("No orders are included.");
    }
}
Using context As New AdventureWorksEntities()
    Dim resultWithoutRelatedObjects = context.Contacts. _
        Include("SalesOrderHeaders"). _
        Select(Function(c) New With {c}).FirstOrDefault()
    If resultWithoutRelatedObjects.c.SalesOrderHeaders.Count = 0 Then
        Console.WriteLine("No orders are included.")
    End If
End Using

Чтобы получить связанные объекты, не приводите возвращаемые типы к анонимному типу.

using (AdventureWorksEntities context = new AdventureWorksEntities())
{
    var resultWithRelatedObjects =
        context.Contacts.Include("SalesOrderHeaders").Select(c => c).FirstOrDefault();
    if (resultWithRelatedObjects.SalesOrderHeaders.Count != 0)
    {
        Console.WriteLine("Orders are included.");
    }
}
Using context As New AdventureWorksEntities()
    Dim resultWithRelatedObjects = context.Contacts. _
        Include("SalesOrderHeaders"). _
        Select(Function(c) c).FirstOrDefault()
    If resultWithRelatedObjects.SalesOrderHeaders.Count <> 0 Then
        Console.WriteLine("Orders are included.")
    End If
End Using

См. также