2010-01-28 14 views
5

Sto usando LINQ alle entità.Perché questa espressione di linq non funziona?

Ho un tavolo chiamato Studente; ha ID e nome come colonne. L'ID è una chiave primaria.

Vorrei poter selezionare il nome dello studente e ottenere il numero di studenti con lo stesso nome.

Quindi, ad esempio, avrei questo come dati della tabella.

ID Name 
1 Bob 
2 Will 
3 Bob 

Dopo aver eseguito la query, restituirei un elenco di oggetti studenti simili a questo.

Name Quantity 
Bob  2 
Will 1 

Suppongo che sia un po 'simile a come funziona la pagina Tag di stackoverflow; Ha il nome e la quantità.

In ogni caso, ho creato una classe parziale chiamata Student.cs in cui ho aggiunto una proprietà Quantity come questa.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Web; 

namespace MySite.Models 
{ 
    public partial class Student 
    { 
     private int _quantity; 

     public int Quantity 
     { 
      get { return _quantity; } 
      set { _quantity = value; } 
     } 
    } 
} 

sono arrivato fino a questo, ma sto ottenendo un errore ..

public IQueryable<Student> FindStudentsDistinctWithQuantity() 
    { 
     /*SELECT Name, COUNT(Name) AS Quantity 
     FROM Student 
     GROUP BY Name*/ 

     var students= (from s in db.Students 
        group s by s.Name into g 
        select new {Name = g.Key, Quantity = g.Count()});    

     return students; 
    } 

l'errore che sto ottenendo dice qualcosa del tipo Impossibile convertire dal tipo anonimo alla lista degli studenti. Ha qualcosa a che fare con esso non riconoscendo il campo della quantità che ho aggiunto nella classe parziale?

Grazie!

+0

Alcuni consigli generali: smettere di usare var fino ad arrivare più esperienza . Se avessi provato a usare 'IQueryable = (da ...' avresti azzerato molto più velocemente sul problema. –

risposta

4

modificare il tipo di Student a guardare come questo:

public partial class Student 
{ 
    public Int32 Quantity { get; set; } 
    public String Name { get; set; } 
} 

E I dati di questo aspetto:

var students = from s in db.Students 
       group s by s.Name into g 
       select new Student { 
        Name = g.Key, 
        Quantity = g.Count() }; 

Il metodo restituisce un IQueryable<Student> ma si sta attualmente restituendo un IQueryable<T> di un tipo anonimo proiettato.

È necessario refactoring del tipo Student di avere una proprietà di tipo NameString e quindi progettare nuove istanze della vostra Student tipo dall'espressione in modo che il tipo di ritorno della vostra espressione corrisponderà il tipo di ritorno del metodo.

+0

Non è un "refactoring" per correggere un bug.E lo Studente come ha definito sopra ha un Nome, perché è una classe parziale che si estende dal suo oggetto Linq che ha Nome e ID. Ora l'estensione di Student per aggiungere una quantità è decisamente discutibile ma .... –

+0

È una cattiva idea estendere la classe Student per aggiungere una proprietà quantità? Non ho una colonna di quantità nel database per studenti. Mi piacerebbe restituire un elenco di oggetti Studente così nella mia vista (che eredita l'oggetto Studente) potrei facilmente visualizzare la quantità. C'è un modo diverso/migliore di farlo? – hanesjw

+0

Sì, è una cattiva idea - stavo guardando il codice e non l'intera immagine. –

2

Il problema è che non si stanno restituendo studenti: si sta tentando di restituire un tipo anonimo dalla propria funzione. Questo non è permesso.

creare una classe per rappresentare il risultato e utilizzare new MyClass { ... } invece di new { ... } nella query, e cambiare il metodo per restituire IQueryable<MyClass> invece di IQueryable<Student>.

È possibile ad esempio creare una classe denominata StudentNameAndResults.

class StudentNameAndResults 
{ 
    public string Name { get; set; } 
    public int Quantity { get; set; } 
} 

In alternativa, è possibile restituire il risultato come dizionario o IEnumarable di IGrouping. Ad esempio:

public IDictionary<string, int> FindStudentsDistinctWithQuantity() 
{ 
    Database db = new Database(); 
    var students= (from s in db.Students 
       group s by s.Name into g 
       select new {Name = g.Key, Quantity = g.Count()}); 

    return students.ToDictionary(s => s.Name, s => s.Quantity); 
} 

Inoltre, la proprietà creata utilizza la sintassi dettagliata dei giorni pre-C# 3.0. Ora è possibile utilizzare auto-implemented properties se non hai bisogno di alcuna logica:

public int Quantity { get; set; } 
1
var students= (from s in db.Students 
        group s by s.Name into g 
        select new {Name = g.Key, Quantity = g.Count()}); 

Questo è un tipo anonimo, non IQueryable<Student>. O avete bisogno di tornare System.Object, o avete bisogno di tornare IQueryable<Student> come segue ...

return from s in db.Students 
     group s by s.Name into g 
     select new Student{Name = g.Key, Quantity = g.Count()}; 

Dove studente definisce le proprietà utilizzate nella initializtion.

+0

Sebbene sia possibile restituire un tipo anonimo come System.Object, non è di grande aiuto in quanto non è possibile utilizzare i campi a meno che non lo si invii al sottotipo corretto e non si conosca il tipo. Non raccomanderei di farlo. –

2

I select new parole chiave stanno causando la forma dei dati per cambiare, il che significa che la query LINQ non restituirà un IQueryable <Studente>, ma piuttosto un tipo anonimo che contiene la proprietà "quantità" "Nome" e. Se lo cambi per restituire un tipo concreto piuttosto che anonimo, sarai in grado di recuperare i dati nel modulo che desideri.

public class StudentGrouping { 
    public string Name { get; set; } 
    public int Quantity { get; set; } 
} 

public IQueryable<StudentGrouping> FindStudentsDistinctWithQuantity() 
{ 
    /*SELECT Name, COUNT(Name) AS Quantity 
    FROM Student 
    GROUP BY Name*/ 

    var students= (from s in db.Students 
       group s by s.Name into g 
       select new StudentGrouping { 
        Name = g.Key, 
        Quantity = g.Count() 
       }).AsQueryable();    

    return students; 
} 
+0

Non è sicuro che AsQueryable() alla fine funzioni? –

2

La funzione restituisce Student

public IQueryable<Student> FindStudentsDistinctWithQuantity(){ ... } 

Ma la query LINQ restituisce un nuovo tipo che contiene un nome e un int (conteggio)

   >>> select new {Name = g.Key, Quantity = g.Count()});    

y-provare selezionare nuovo Student { Nome = g.Key, Quantità = g.Count()}

1

Si sta facendo una proiezione nella query linq. Se si posiziona il puntatore del mouse su var students all'interno, verrà visualizzata una raccolta di un tipo anonimo.

Se si desidera restituire un IQueryabley<Student> quello che devi fare:

var students= from s in db.Students 
        group s by s.Name into g 
        select s.Key; 

Non c'è modo metodi esterni possono conoscere il tipo anonimo che hai creato nel tuo esempio precedente, in modo da non essere in grado per restituire una raccolta tipizzata.

Con il metodo ho suggerito si sarà ancora in grado di fare una proiezione sul valore di ritorno del metodo seguito, dal momento che IQueryable è componibile fino alla prima di enumerazione:

var students = FindStudentsDistinctWithQuantity(); 
var namesAndQunatity = from s in students select new {s.Name, s.Quantity}; 
+0

Penso che il problema è che 'Studente' non ha una' Quantità' nel database e sta provando ad aggiungerlo alla classe e impostarlo all'interno di questa query. Quindi se hai usato il tuo suggerimento, direi che 'Quantity' sarebbe sempre impostata su 0. –

2

del metodo del valore di ritorno legami le Raccolta "students" su IQueryable<Student> ma ... l'espressione di Linq sta creando un IQueryable<some anonymous type> e non vi è alcuna conversione tra i due.

Si può ottenere un bambino ulteriore passo avanti, modificando la parte di selezione per essere:

select new Student() {....} 

Speriamo che questo aiuta,

Tyler

Problemi correlati