2010-01-28 16 views
5

Ich verwende LINQ zu Entitäten.Warum funktioniert dieser linq-Ausdruck nicht?

Ich habe eine Tabelle namens Student; Es hat ID und Name wie es Spalten ist. ID ist ein Primärschlüssel.

Ich möchte in der Lage sein, den Namen des Schülers auszuwählen und die Anzahl der Schüler mit dem gleichen Namen zu erhalten.

Also zum Beispiel würde ich dies als meine Tabellendaten haben.

ID Name 
1 Bob 
2 Will 
3 Bob 

Nach Ausführen der Abfrage würde ich eine Liste der Schüler Objekte zurückgeben, die so aussehen.

Name Quantity 
Bob  2 
Will 1 

Ich denke, es ist ähnlich wie die Tags-Seite von Stackoverflow funktioniert; Es hat den Namen und die Menge.

Anyways, ich habe eine Teilklasse namens Student.cs erstellt, in der ich eine Quantity-Eigenschaft wie folgt hinzugefügt habe.

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

ich kam mit dieser, aber ich erhalte eine Fehlermeldung ..

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

Der Fehler, ich bin so wie immer etwas kann nicht konvertieren von Typ Anonymous auf die Schülerliste. Hat es etwas damit zu tun, das Mengenfeld, das ich in der Teilklasse hinzugefügt habe, nicht zu erkennen?

Danke!

+0

Einige allgemeine Hinweise: stop var, bis Sie mehr Erfahrung zu bekommen . Wenn Sie versucht hätten, 'IQueryable = (from ...' zu verwenden, hätten Sie das Problem viel schneller lösen können. –

Antwort

4

Ihre Student Art ändern wie folgt aussehen:

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

Und Ihre Abfrage wie folgt aussehen:

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

Ihre Methode gibt eine IQueryable<Student> zurück, aber Sie geben derzeit eine IQueryable<T> eines projizierten anonymen Typs zurück.

Sie benötigen Student Typen Refactoring String eine Name Eigenschaft des Typs haben und dann neue Instanzen Ihres Student Typen aus dem Ausdruck projizieren, so dass der Rückgabetyp des Ausdrucks wird die Rückkehr Typ Ihrer Methode entspricht.

+0

Es ist kein "Refactoring", um einen Fehler zu beheben.Und Student, wie er oben definiert wurde, hat einen Namen, weil es sich um eine Teilklasse handelt, die sich von seinem Linq-Objekt mit Name und ID erstreckt. Jetzt Erweiterung Student, um eine Menge hinzuzufügen, ist definitiv fraglich, aber .... –

+0

Ist es eine schlechte Idee, die Student-Klasse zu erweitern, um eine Menge Eigenschaft hinzuzufügen? Ich habe keine Mengenspalte in der Datenbank für Studenten. Ich möchte eine Liste von Student-Objekten zurückgeben, sodass ich in meiner View (die das Student-Objekt erbt) die Menge leicht anzeigen kann. Gibt es einen anderen/besseren Weg? – hanesjw

+0

Ja, das ist eine schlechte Idee - ich habe mir den Code angeschaut und nicht das ganze Bild. –

2

Das Problem ist, dass Sie keine Studenten zurückgeben - Sie versuchen, einen anonymen Typ von Ihrer Funktion zurückzugeben. Das ist nicht erlaubt.

Erstellen Sie eine Klasse Ihr Ergebnis zu vertreten und new MyClass { ... } statt new { ... } in Ihrer Abfrage zu verwenden, und die Methode ändern IQueryable<MyClass> statt IQueryable<Student> zurückzukehren. Sie können zum Beispiel eine Klasse namens StudentNameAndResults erstellen.

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

Alternativ könnten Sie einfach das Ergebnis als Wörterbuch zurückzukehren oder eine IEnumarable von IGrouping. Zum Beispiel:

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

Auch die Eigenschaft, die Sie die ausführliche Syntax von Pre-C# 3,0 Tagen erstellt wird. Jetzt können Sie auto-implemented properties verwenden, wenn Sie keine spezielle Logik benötigen:

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

Dies ist ein anonymer Typ, nicht IQueryable<Student>. Entweder müssen Sie System.Object zurückzukehren, oder Sie müssen IQueryable<Student> zurückkehren wie folgt ...

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

Wo Schüler die Eigenschaften in der initializtion verwendet wird, definiert.

+0

Während Sie einen anonymen Typ als System.Object zurückgeben können, hilft das wenig, da Sie die Felder nicht verwenden können, wenn Sie sie nicht auf den richtigen Subtype anwenden und Sie nicht wissen, um welchen Typ es sich handelt. Ich würde das nicht empfehlen. –

2

Die select new Schlüsselwörter verursachen die Form der Daten zu ändern, was bedeutet, die LINQ-Abfrage wird keine IQueryable < Schüler zurückkehren >, sondern eine anonyme Art der „Name“ und „Menge“ Eigenschaften. Wenn Sie es so ändern, dass ein konkreter Typ anstelle eines anonymen Typs zurückgegeben wird, können Sie die Daten in der von Ihnen gewünschten Form abrufen.

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

Nicht sicher über das AsQueryable() am Ende - wird das funktionieren? –

2

Ihre Funktion gibt Studenten

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

Aber Ihre Linq Abfrage gibt eine neue Art, die einen Namen und ein Int (count) enthält

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

y-versuchen, neue Schüler wählen { Name = g.Key, Menge = g.Count()}

1

Sie machen eine Projektion in Ihrer Linq-Abfrage. Wenn Sie den Mauszeiger über var students innerhalb von vs bewegen, sehen Sie eine Sammlung eines anonymen Typs.

Wenn Sie ein IQueryabley<Student> Sie tun müssen zurückkehren wollen:

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

Es gibt keine Möglichkeit außerhalb Methoden über den anonymen Typen wissen, können Sie in Ihrem vorherigen Beispiel erstellt haben, so dass Sie nicht in der Lage sein, eine typisierte Sammlung zurückgeben.

Mit der Methode, die ich vorgeschlagen, dass Sie noch in der Lage sein wird, später eine Projektion auf den Rückgabewert Ihrer Methode zu tun, da IQueryable, bis die erste Aufzählung zusammensetzbare ist:

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

Ich denke, das Problem ist, dass der 'Student' keine' Menge' in der Datenbank hat und er versucht, sie der Klasse hinzuzufügen und sie in dieser Abfrage zu setzen. Wenn Sie also Ihren Vorschlag verwenden, würde ich annehmen, dass "Menge" immer auf 0 gesetzt wird. –

2

Der Rückgabewert Bindungen Methode der "" Sammlung zu IQueryable<Student> aber ... der Linq-Ausdruck erstellt eine IQueryable<some anonymous type>, und es gibt keine Konvertierung zwischen den beiden.

Sie ein Baby, indem Sie Ihren ausgewählten Teil Schritt weiter kommen können:

select new Student() {....} 

Hoffentlich hilft,

Tyler

Verwandte Themen