Problemi noti e considerazioni relativi a LINQ to Entities

In questa sezione vengono fornite informazioni sui problemi noti relativi alle query LINQ to Entities.

  • Perdita delle informazioni di ordinamento

  • Query nidificate

  • Mancato supporto dei valori integer senza segno

  • Errori di conversione dei tipi

  • Riferimento a chiusure non scalari non supportato

Perdita delle informazioni di ordinamento

Se vengono eseguite operazioni aggiuntive dopo un'operazione di ordinamento, l'ordinamento potrebbe non venire mantenuto in tali operazioni aggiuntive. Sono incluse operazioni come Select o Where, come illustrato nell'esempio seguente:

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 proiezione di colonne in un tipo anonimo comporta la perdita delle informazioni di ordinamento in alcune query eseguite su un database di SQL Server 2005 con un livello di compatibilità impostato su "80". Questo avviene quando un nome di colonna nell'elenco di ordinamento corrisponde a un nome di colonna nel selettore, come illustrato nell'esempio seguente:

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

}

I metodi First e FirstOrDefault che accettano un'espressione come parametro di input non consentono di mantenere l'ordinamento.

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

Query nidificate

L'ordinamento nelle query nidificate non viene mantenuto. Nell'esempio seguente l'ordinamento in base al cognome viene perso quando viene chiamato il secondo metodo 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);
    }
}

Nell'esempio seguente viene chiamato il metodo OrderBy prima che il metodo Where produca un'istruzione nidificata quando la query viene convertita in alberi dei comandi canonici e l'ordinamento viene perso:

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

}

Mancato supporto dei valori integer senza segno

Non è possibile specificare un tipo integer senza segno in una query LINQ to Entities in quanto in Entity Framework non sono supportati i valori integer senza segno. Se si specifica un valore integer senza segno, viene generata un'eccezione ArgumentException durante la conversione dell'espressione di query, come illustrato nell'esempio seguente. In questo esempio viene eseguita una query per un ordine con 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);
    }
}

Errori di conversione dei tipi

In Visual Basic quando una proprietà è mappata a una colonna di tipo bit SQL Server con un valore 1 utilizzando la funzione CByte, viene generato un evento SqlException con un messaggio di errore di overflow aritmetico. Nell'esempio seguente viene eseguita una query sulla colonna Product.MakeFlag nel database di esempio AdventureWorks e viene generata un'eccezione quando i risultati della query vengono scorsi.

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

Riferimento a chiusure non scalari non supportato

Il riferimento in una query a un chiusura non scalare, ad esempio un'entità, non è supportato. Durante l'esecuzione di una query di questo tipo, viene generata un'eccezione NotSupportedException, seguita dal messaggio "Impossibile creare un valore di costante di tipo 'Tipo di chiusura'. In questo contesto solo supportati solo i tipi primitivi ('ad esempio Int32, String e GUID')".

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

Vedere anche

Altre risorse

Esecuzione di query con LINQ to Entities