LINQ to Entities 的已知问题和注意事项

本节提供有关 LINQ to Entities 查询的已知问题的信息。

  • 排序信息丢失

  • 嵌套查询

  • 不支持无符号整数

  • 类型转换错误

  • 不支持引用非标量闭包

排序信息丢失

如果在排序操作之后执行了任何其他操作,则不能保证这些附加操作中会保留排序结果。这些操作包括 SelectWhere 等,如下面的示例所示:

Using AWEntities As New AdventureWorksEntities()

    ' In this query, the ordering is not preserved because Distinct
    ' is called after OrderByDescending.
    Dim productsList = _
    From product In AWEntities.Product _
    Order By product.Name Descending _
    Select product.Name _
    Distinct

    Console.WriteLine("The list of products:")
    For Each productName In productsList
        'Console.WriteLine(productName)
    Next

    ' In this query, the ordering is preserved because 
    ' OrderByDescending is called after Distinct.
    Dim productsList2 = _
    From product In AWEntities.Product _
    Select product.Name _
    Distinct _
    Order By Name Descending

    Console.WriteLine("The list of products:")
    For Each productName In productsList2
        Console.WriteLine(productName)
    Next

End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    // In this query, the ordering is not preserved because Distinct
    // is called after OrderByDescending.
    IQueryable<string> productsList = AWEntities.Product
        .OrderByDescending(p => p.Name)
        .Select(p => p.Name)
        .Distinct();

    Console.WriteLine("The list of products:");
    foreach (string productName in productsList)
    {
        Console.WriteLine(productName);
    }

    // In this query, the ordering is preserved because 
    // OrderByDescending is called after Distinct.
    IQueryable<string> productsList2 = AWEntities.Product
        .Select(p => p.Name)
        .Distinct()
        .OrderByDescending(p => p);

    Console.WriteLine("The list of products:");
    foreach (string productName in productsList2)
    {
        Console.WriteLine(productName);
    }

}

如果将列投影到匿名类型,则会导致对兼容级别设置为“80”的 SQL Server 2005 数据库所执行的某些查询中会丢失排序信息。当 order-by 列表中的列名与选择器中的列名相同时,就会发生这种情况,如下面的示例所示:

Using AWEntities As New AdventureWorksEntities()
    ' Ordering information is lost when executed against a SQL Server 2005
    ' database running with a compatibility level of "80".
    Dim results = AWEntities.Contact.SelectMany(Function(c) c.SalesOrderHeader) _
        .OrderBy(Function(c) c.SalesOrderDetail.Count) _
        .Select(Function(c) New With {c.SalesOrderDetail.Count})

    For Each result In results
        Console.WriteLine(result.Count)
    Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    // Ordering information is lost when executed against a SQL Server 2005
    // database running with a compatibility level of "80".
    var results = AWEntities.Contact.SelectMany(c => c.SalesOrderHeader)
        .OrderBy(c => c.SalesOrderDetail.Count)
        .Select(c => new { c.SalesOrderDetail.Count });

    foreach (var result in results)
        Console.WriteLine(result.Count);

}

采用表达式作为输入参数的 FirstFirstOrDefault 方法不保留顺序。

Using AWEntities As New AdventureWorksEntities()
    ' The First() and FirstOrDefault() methods which take expressions
    ' as input parameters do not preserve order.
    Dim orders = AWEntities.SalesOrderHeader _
            .Where(Function(c) c.TotalDue = 11.039) _
            .OrderByDescending(Function(c) c.SalesOrderID) _
            .Select(Function(c) c)

    Console.WriteLine("The ordered results:")
    For Each order As SalesOrderHeader In orders
        Console.WriteLine("ID: {0}  Total due: {1}", order.SalesOrderID, order.TotalDue)
    Next

    Dim result As SalesOrderHeader = AWEntities.SalesOrderHeader _
            .Where(Function(c) c.TotalDue = 11.039) _
            .OrderByDescending(Function(c) c.SalesOrderID) _
            .First(Function(c) c.SalesOrderID > 500)

    Console.WriteLine("")
    Console.WriteLine("The result returned is not the first result from the ordered list.")
    Console.WriteLine("ID: {0}  Total due: {1}", result.SalesOrderID, result.TotalDue)
End Using
    using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
    {
        // The First() and FirstOrDefault() methods which take expressions
        // as input parameters do not preserve order.
        var orders = AWEntities.SalesOrderHeader
            .Where(c => c.TotalDue == 11.039M)
            .OrderByDescending(c => c.SalesOrderID)
            .Select(c => c);

        Console.WriteLine("The ordered results:");
        foreach (SalesOrderHeader order in orders)
            Console.WriteLine("ID: {0} \t Total due: {1}", order.SalesOrderID, order.TotalDue);

        SalesOrderHeader result = AWEntities.SalesOrderHeader
            .Where(c => c.TotalDue == 11.039M)
            .OrderByDescending(c => c.SalesOrderID)
            .First(c => c.SalesOrderID > 500);

        Console.WriteLine("");
        Console.WriteLine("The result returned is not the first result from the ordered list.");
        Console.WriteLine("ID: {0} \t Total due: {1}", result.SalesOrderID, result.TotalDue);
    }
}

嵌套查询

不保留嵌套查询中的排序。在下面的示例中,调用第二个 Select 方法时,按姓氏排序的结果将会丢失:

Using AWEntities As New AdventureWorksEntities()
    Dim contacts = AWEntities.Contact _
            .OrderBy(Function(x) x.LastName) _
            .Select(Function(x) x) _
            .Select(Function(x) x.LastName)

    For Each contact In contacts
        Console.WriteLine(contact)
    Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    // Return all contacts, ordered by last name.
    IQueryable<string> contacts = AWEntities.Contact
        .OrderBy(x => x.LastName)
        .Select(x => x)
        .Select(x => x.LastName);

    foreach (var c in contacts)
    {
        Console.WriteLine(c);
    }
}

在下面的示例中,将查询转换为规范命令目录树后,在 Where 方法之前调用 OrderBy 方法会生成一个嵌套语句,并且排序结果会丢失:

Using AWEntities As New AdventureWorksEntities()
    ' Return all contacts, ordered by last name. The OrderBy before
    ' the Where produces a nested query when translated to 
    ' canonical command trees and the ordering by last name is lost.
    Dim contacts = AWEntities.Contact _
            .OrderBy(Function(x) x.LastName) _
            .Where(Function(x) x.FirstName = "John") _
            .Select(Function(x) x)

    For Each c In contacts
        Console.WriteLine(c.LastName & ", " & c.FirstName)
    Next

    ' Return all contacts, ordered by last name.
    Dim contacts2 = AWEntities.Contact _
            .Where(Function(x) x.FirstName = "John") _
            .OrderBy(Function(x) x.LastName) _
            .Select(Function(x) x)

    For Each c In contacts2
        Console.WriteLine(c.LastName & ", " & c.FirstName)
    Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    // Return all contacts, ordered by last name. The OrderBy before
    // the Where produces a nested query when translated to 
    // canonical command trees and the ordering by last name is lost.
    IQueryable<Contact> contacts = AWEntities.Contact
        .OrderBy(x => x.LastName)
        .Where(x => x.FirstName == "John")
        .Select(x => x);

    foreach (var c in contacts)
    {
        Console.WriteLine(c.LastName + ", " + c.FirstName);
    }

    // Return all contacts, ordered by last name.
    IQueryable<Contact> contacts2 = AWEntities.Contact
        .Where(x => x.FirstName == "John")
        .OrderBy(x => x.LastName)
        .Select(x => x);

    foreach (var c in contacts2)
    {
        Console.WriteLine(c.LastName + ", " + c.FirstName);
    }

}

不支持无符号整数

由于 实体框架 不支持无符号整数,因此不支持在 LINQ to Entities 查询中指定无符号整数类型。如果指定无符号整数,则在查询表达式转换过程中会引发 ArgumentException 异常,如下面的示例所示。此示例查询其 ID 为 48000 的订单。

Using AWEntities As New AdventureWorksEntities()
    Dim saleId As UInteger = UInt32.Parse("48000")

    Dim sales As ObjectQuery(Of SalesOrderDetail) = AWEntities.SalesOrderDetail
    Dim query = _
        From sale In sales _
        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
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    uint s = UInt32.Parse("48000");

    ObjectQuery<SalesOrderDetail> sales = AWEntities.SalesOrderDetail;

    IQueryable<SalesOrderDetail> query = from sale in sales
                                         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: {0}", ex.Message);
    }
}

类型转换错误

在 Visual Basic 中,如果使用 CByte 函数将某一属性映射到值为 1 且为 SQL Server 位类型的列,则会引发 SqlException 并显示“发生算术溢出错误”消息。下面的示例查询 AdventureWorks 示例数据库中的 Product.MakeFlag 列,在循环访问查询结果时引发一个异常。

Using AWEntities As New AdventureWorksEntities()
    Dim productsList = _
        From product In AWEntities.Product _
        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 异常,并显示消息“无法创建类型为‘闭包类型’的常量值。此上下文中仅支持基元类型(‘如 Int32、String 和 Guid’)。”[Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.]

Using AWEntities As New AdventureWorksEntities()

    Dim contact As Contact = AWEntities.Contact.FirstOrDefault()

    ' Referencing a non-scalar closure in a query will
    ' throw an exception when the query is executed.
    Dim contacts = From c In AWEntities.Contact _
                   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
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
    Contact contact = AWEntities.Contact.FirstOrDefault();

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

    try
    {
        foreach (string name in contacts)
        {
            Console.WriteLine("Name: ", name);
        }
    }
    catch (NotSupportedException ex)
    {
        Console.WriteLine(ex.Message);
    }
}

另请参见

其他资源

使用 LINQ to Entities 进行查询