Share via


Problèmes connus dans LINQ to Entities et éléments à prendre en considération

Cette section fournit des informations sur les problèmes connus au niveau des requêtes LINQ to Entities.

  • Perte des informations de tri

  • Requêtes imbriquées

  • Entiers non signés non pris en charge

  • Erreurs de conversion de type

  • Référencement de clôtures non scalaires non pris en charge

Perte des informations de tri

Si d'autres opérations sont effectuées consécutivement à une opération de tri, il n'est pas assuré que ces opérations supplémentaires préserveront l'ordre de tri. Il peut s'agir d'opérations Select ou Where, comme l'illustre l'exemple suivant :

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);
    }

}

La projection de colonnes dans un type anonyme entraînera la perte des informations de tri dans certains requêtes exécutées sur une base de données SQL Server 2005 définie à un niveau de compatibilité de « 80 ». Cela se produit lorsqu'un nom de colonne figurant dans la liste Order by correspond à un nom de colonne dans le sélecteur, comme l'illustre l'exemple suivant :

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);

}

Les méthodes First et FirstOrDefault, qui considèrent une expression comme un paramètre d'entrée, ne conservent pas l'ordre de tri.

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);
    }
}

Requêtes imbriquées

L'ordre de tri dans les requêtes imbriquées n'est pas préservé. Dans l'exemple suivant, l'ordre de tri par nom de famille est perdu lorsque la deuxième méthode Select est appelée :

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);
    }
}

Dans l'exemple suivant, l'appel de la méthode OrderBy avant la méthode Where génère une instruction imbriquée lorsque la requête est traduite en arborescences de commandes canoniques, et l'ordre de tri est perdu :

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);
    }

}

Entiers non signés non pris en charge

La définition d'un type d'entier non signé dans une requête LINQ to Entities n'est pas prise en charge, car Entity Framework ne prend pas en charge les entiers non signés. Si vous spécifiez un entier non signé, une exception ArgumentException est levée pendant la traduction des expressions de la requête, comme l'illustre l'exemple suivant. Dans cet exemple, la requête vise à extraire une commande dont le numéro est 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);
    }
}

Erreurs de conversion de type

En Visual Basic, lorsqu'une propriété est mappée à une colonne de type bit SQL Server avec une valeur de 1 à l'aide de la fonction CByte, une exception SqlException est levée avec un message « Erreur de dépassement arithmétique ». L'exemple suivant interroge la colonne Product.MakeFlag dans l'exemple de base de données AdventureWorks et une exception est levée lorsque les résultats de la requête sont parcourus.

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

Référencement de clôtures non scalaires non pris en charge

Le référencement d'une fermeture non scalaire, telle qu'une entité, dans une requête n'est pas pris en charge. Lorsqu'une telle requête s'exécute, une exception NotSupportedException est levée avec un message indiquant « Impossible de créer une valeur constante de type « Type de clôture ». Seuls les types primitifs (« Int32, String et Guid ») sont pris en charge dans ce contexte. »

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);
    }
}

Voir aussi

Autres ressources

Exécution de requêtes avec LINQ to Entities