Laden...

[erledigt] Entity Framework: Abfrage in eigener Collection speichern

Erstellt von m.grauber vor 13 Jahren Letzter Beitrag vor 13 Jahren 3.647 Views
M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 13 Jahren
[erledigt] Entity Framework: Abfrage in eigener Collection speichern

Hallo,

ich nutze SQL-Server 2000-2008, C#, VS 2010 und arbeite über das Entity Framework.

Ich möchte eine Abfrage in einer Collection speichern, die ich selbst erstellt habe.


ObjectQuery<Kunden> Kunden = EModell.Kunden;
var query =  from A in Kunden  select new { A.Name, A.ID };

public class oDisp    // Fehlt hier noch ein Interface?
{
  public string Name {get;set;}
  public int ID {get;set;}
  public oDisp  // Konstruktor ist doch da!
    {
       Name="";
       ID=0;
    }
}

// Hier tritt der Fehler auf: 
ObjectSet<oDisp> query2 = new ObjectSet<oDisp>(); // Fehler: "Für den Typ "System.Data.Objects.ObjectSet<TEntity>" sind keine Konstruktoren definiert."

query2=query  // Hier soll query in query2 abgelegt werden, weil ich zukünftig mit query2 weiterarbeiten will
oder:
query2 = (ObjectSet<oDisp>)query; // ist das notwendig?


DataGrid.ItemsSource=query   // Funktioniert Problemlos: Werte werden angezeigt
query.Dispose();                     // gibt es nicht - warum nur?

DataGrid.ItemsSource=query2  // Das möchte ich! - Wenn obiger Fehler weg ist

Ist ObjectSet<> überhaupt die richtige Wahl? Und was habe ich falsch gemacht?

Vielen Dank für Eure Hilfe!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

5.299 Beiträge seit 2008
vor 13 Jahren

ich denke, wenn ObjectSet keinen Konstruktor hat, dann geht damit wohl nichts. Ah, vlt doch: scheinbar muß man sich von einem ObjectContext ein ObjectSet erstellen lassen.

mir ist aber auch unklar, ob du queries speichern willst, oder Ergebnisse von queries.

jedenfalls das ist eine query, die anonyme typen ermittelt:


var query =  from A in Kunden  select new { A.Name, A.ID };

sowas kannste nicht speichern.

(ich sage es gelegentlich, und werd manchmal angemeckert, weil ich anonyme typen ziemlich nutzlos finde, weil man sie nur lokal verwenden kann - was will man mit daten, die man nicht weiter geben kann?)

das wäre eine query mit ordentlichen typen:


var query =  from A in Kunden  select new oDisp(){Name=A.Name, ID=A.ID };

und die kannste etwa in einer List<oDisp> speichern:

var oDisps=query.ToList();

(wobei oDisp ein unnötig kryptischer Name für eine Klasse ist - v.a. was bedeutet der Präfix "o"?)
(auch der Konstruktor von oDisp ist in dieser Form vmtl. üflüssig)

Der frühe Apfel fängt den Wurm.

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 13 Jahren
Entity Framework: Abfrage in eigener Collection speichern

Hallo Erfinder des Rads!

vielen Dank für Deine Unterstützung! Das hat mir bereits gut weitergeholfen! Der Konstruktor in oDisp ist nun draußen. (Der Name wurde nur schnellschnell gewählt und wird später geändert)

und die Query ist nun auch korrigiert:
var query = from A in Kunden select new oDisp(){Name=A.Name, ID=A.ID };

Ich erstelle bereits direkt in der Klasse des WPF-Windows eine Liste (und natürlich oDisp):
List<oDisp> KundeListe = new List<oDisp>();

Wenn ich nach der Query die Werte in die Liste KundeListe übernehmen will:


KundeListe = (List<oDisp>)query;

erhalte ich jedoch folgenden Fehler:

Das Objekt des Typs "System.Data.Objects.ObjectQuery1[MyN.KundeForm+oDisp]&quot; kann nicht in Typ &quot;System.Collections.Generic.List1[MyN.Kunde]" umgewandelt werden.

(Inner Exception ist null)

* Warum klappt hier nicht die Konvertierung?
* Ist es besser statt der List<Kunde> eine ObservableList<Kunde> oder etwas passenderes zu nehmen?

Tausend Dank!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

5.299 Beiträge seit 2008
vor 13 Jahren

die fehlermeldung sagt es dir doch:

eine ObjectQuery<T> ist keine List<T>, das kann man auch durch casten nicht erzaubern.

KundeListe ist eine List<T>, und query ist eine ObjectQuery<T>, und

KundeListe = (List<oDisp>)query;

ist also Quatsch.

wie man die _ergebnisse _der query in einer List speichert schrieb ich ja schon - ist eiglich nicht kompliziert 😉

Der frühe Apfel fängt den Wurm.

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 13 Jahren
Entity Framework: Abfrage in eigener Collection speichern

Hallo Erfinder des Rads,

ich weiß, du hast geschrieben:


var oDisps=query.ToList();

Mein Problem ist nur, dass

Ich erstelle bereits direkt (im Konstruktor) in der Klasse des WPF-Windows eine Liste (und natürlich oDisp) List<oDisp> KundeListe = new List<oDisp>();

Wenn die Form geöffnet wird, existiert bereits ohne die anonyme Abfrage meine Liste. Daher muss ich vorher den Datentyp definieren. (Egal ob List, ObservableCollection oder irgend etwas anderes)

Erst wenn der Anwender ein Filterkriterium auswählt (z. B. alle Kunden mit "A"), kann ich mir die Daten vom Server holen. Da dies dann in einen anonymen Typen (mit var x =) passiert, muss ich diesen anonymen Typen in meine Liste übernehmen (und konvertieren), die bereits seit dem Öffnen der Form vorhanden ist. Evtl. kann ich ja gleich in diese Liste selektieren? - Das habe ich jedoch noch nicht hinbekommen.

Diese umständliche Vorgehensweise benötige ich, weil ich die "KundeListe" an meine WPF-Controls binde, evtl. für weitere Aktionen, Änderungen oder für einen Valueconverter bereits fixieren möchte. Auch bei späteren Umsortierungen etc. möchte ich dann mit dieser Liste arbeiten.

* Nur wie kann ich entweder direkt in eine vorhandene Liste selektieren oder die anonyme Liste in meine Liste konvertieren?

* Welcher Listentyp ist der beste dafür (IList, ObservableCollection, etc.?)

Vielen Dank!

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

5.299 Beiträge seit 2008
vor 13 Jahren

vmtl. musstedann die ergebnisse der query in die List<oDisp> umkopieren.


//[ObjectQuery<anonymousType> query;]
foreach(var itm in query)KundeListe.Add(new oDisp(){Name=itm.Name, ID=itm.ID});

ich hab tatsächlich weder vom entityFramewok noch von WPF die Ahnung, zweifel aber mal frech an, dass die query die zwingend als anonyme typen holen muss.
Wenn man dem Query-Generator vernünftige queries zu generieren beibringen könnte, wärs umkopieren bisserl einfacher:


//[ObjectQuery<oDisp> query;]
foreach(var disp in query)KundeListe.Add(disp);

Das erste Beispiel ist aufgrund der Typ-Anonymität nur in der methode möglich, wo query instanziert wurde, das 2. kann überall stattfinden.

Man kann glaub auch mit reflection und propertyInfos auf anonyme Typen losgehen, das ist aber ein etwas monströser und unperformanter workaround. Wie gesagt: ohne groß im Thema eingearbeitet zu sein, scheinen mir queries, die anonyme Typen auswerfen, eher ein DesignFehler.

Der frühe Apfel fängt den Wurm.

T
146 Beiträge seit 2004
vor 13 Jahren

schreib doch einfach:

MeineVorherSchonGenerierteLeereListe = query.ToList<oDisp>();

Das sollte die ganze zaubere sein.

5.299 Beiträge seit 2008
vor 13 Jahren

nee, wenner seine MVP-Controls an MeineVorherSchonGenerierteLeereListe gebunden hat, und die dann durch eine neue ersetzt, dann ist in den Controls natürlich nix von den neuen Daten zu sehen, weil die sind ja an die alte MeineVorherSchonGenerierteLeereListe gebunden.

@m.grauber: ich glaub auch, es muß eine ObservableCollection sein, weil die Controls sonst nix mitkriegen vonne Datenänderung.

gibts in wpf nicht etwas der winforms.bindingsource vergleichbares?
da kann man seine controls an die bindingsource binden, und wenn man die datasource der bindingsource austauscht, sind damit autom. alle angeschlossenen control-daten ausgetauscht.

Der frühe Apfel fängt den Wurm.

T
146 Beiträge seit 2004
vor 13 Jahren

Das geht zum Beispiel mit einer CollectionViewSource, aber um darauf genauer einzugehen, bräuchte man mehr Infos vom TE über seine Bindings.

Aber warum sollte er nicht eine Liste von seinem definierten Typ durch eine andere ersetzen können? wenn er das ganze über irgendwas mit INotifyPropertyChanged gebunden hat, so kriegt sein UI das doch mit, und seine Liste(Anzeige) aktualisiert sich von selber.

Also ich handhabe das so und es funktioniert einwandfrei.

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 13 Jahren

Hallo Erfinder des Rades und Tom Leech!

Vielen Dank für die super Hilfe! Es klappt nun tatsächlich mit:


MeineVorherSchonGenerierteLeereListe = query.ToList<oDisp>();  :thumbsup:

Aber nur wenn "MeineVorherSchonGenerierteLeereListe" vom Typ List<oDisp> ist. Mit "MeineVorherSchonGenerierteLeereListe" als ObservableCollection klappt es ersteinmal nicht, auch nicht mit einem Casting (ObservableCollection<oDisp>) vor der Zuweisung.

Ich hatte die ganze Zeit versucht zu casten und bin daran verzweifelt.

Vielleicht wisst ihr noch einige Hintergrundinfos dazu:

* query.Dispose(); gibt es nicht. Wie kann man diese Query dann wieder entfernen, ohne auf die automatische Garbage Collection zu warten?

* Was macht .NET bei der Zeile "MeineVorherSchonGenerierteLeereListe = query.ToList<oDisp>();" wirklich? Kopiert er die Liste (wg. ToList) oder reicht er das "Originalmaterial" nur konvertiert weiter.

Vielen Dank nochmals!

Grüße

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

5.299 Beiträge seit 2008
vor 13 Jahren

Hallo Erfinder des Rades und Tom Leech!

Vielen Dank für die super Hilfe! Es klappt nun tatsächlich mit:

  
MeineVorherSchonGenerierteLeereListe = query.ToList<oDisp>();  :O  
  

das ist ja sehr eigentümlich. weil weiter oben sagtest du,


KundeListe = query.ToList();

täte nicht klappen (die Angabe des generischen typen <oDisp> ist übrigens entbehrlich)

Aber nur wenn "MeineVorherSchonGenerierteLeereListe" vom Typ List<oDisp> ist. Mit "MeineVorherSchonGenerierteLeereListe" als ObservableCollection klappt es ersteinmal nicht, auch nicht mit einem Casting (ObservableCollection<oDisp>) vor der Zuweisung.

Ich hatte die ganze Zeit versucht zu casten und bin daran verzweifelt. Ja, vlt. solltest du mal Grundlagen lernen, etwa, was casten macht. Am besten vlt. ein buch durcharbeiten, hier auf der site, unter "Ressourcen" sind ein paar kostenlose genannt. Weil wer nicht weiß, was casten macht, hat vmtl. noch weitere Wissenslücken.
Aber ich kann dir auch eben mein Verständnis davon verbraten: Ein Cast weist den Compiler an, ein gegebenes Objekt als etwas anderes, genauer spezifiziertes, aufzufassen. ZB kannst du eine Variable vom Typ Control haben, und der eine Checkbox zuweisen, das ist ja ein Control.
Willst du nun mit der Variable arbeiten, stehen dir zunächst mal nur die Control-Properties zur Verfügung, nicht die spezifischen Checkbox-Eigenschaften. In so einem Fall kann ein Cast dem Compiler mitteilen "Hier ist eine Checkbox drin". Das geht aber nur gut, wenn du der Variable auch eine Checkbox zugewiesen hast. Ist etwa ein Label zugewiesen erhälst du deinen InvalidCast-Fehler, weil ein Label nunmal keine Checkbox ist.
Wie gesagt: lies ein Buch und lass es dir dort besser erklären.

jedenfalls ebenso ist eine List<oDisp> keine ObservableCollection<oDisp>, da hilft kein heulen und kein casten.
Und query.ToList() erzeugt nunmal eine List, keine ObservableCollection.

Im allgemeinen gilt es, casts zu vermeiden, denn es hebt punktuell eine Arbeit des Compilers, Typ-Überprüfung, auf, das ist natürlich ganz prinzipiell ein Sicherheitsrisiko.

Vielleicht wisst ihr noch einige Hintergrundinfos dazu:

* query.Dispose(); gibt es nicht. Wie kann man diese Query dann wieder entfernen, ohne auf die automatische Garbage Collection zu warten?

wenn kein Dispose vorgesehen ist, ist der Programmierer der Klasse offensichtlich der Meinung, dass die automatische Garbage Collection die beste Form der Ressourcenbereinigung ist.

* Was macht .NET bei der Zeile "MeineVorherSchonGenerierteLeereListe = query.ToList<oDisp>();" wirklich? Kopiert er die Liste (wg. ToList) oder reicht er das "Originalmaterial" nur konvertiert weiter.

mein gott, habich das nun nicht mehrfach gesagt? eine query ist keine Liste!
Nagut, es ist im weitesten sinne eine Auflistung, mal so verstanden, dass es etwas ist, welches in einer foreach-schleife eine reihe von (hier: oDisp-) Objekten auswirft. Vermutlich spielt sich im Hintergrund ein mordsmäßiger Zinnober ab, eine Connection wird geöffnet, ein SqlCommand generiert und abgesetzt, und DataReader-Werte werden in oDisp-Objekte eingelesen, wasweißich.
Nur dass das Durchlaufen einer Query enorm aufwändig ist.
Daher ist .ToList() sehr sinnvoll, da wird die Query einmal durchlaufen, dann hat man eine Liste, und die kann man immer wieder und superschnell durchlaufen.

Wennde dagegen eine ObservableCollection oder sonstwas willst, musste halt die query selbst durchlaufen, und die Collection befüllen - das geht ja auch in einer zeile.

Der frühe Apfel fängt den Wurm.

T
146 Beiträge seit 2004
vor 13 Jahren

Aber nur wenn "MeineVorherSchonGenerierteLeereListe" vom Typ List<oDisp> ist. Mit "MeineVorherSchonGenerierteLeereListe" als ObservableCollection klappt es ersteinmal nicht, auch nicht mit einem Casting (ObservableCollection<oDisp>) vor der Zuweisung.

Dafür gibt es in C# Extension Methods. Ich verwende folgende Methode, um ObservableCollection<T> aus queries zu machen:


public static class CollectionExtensions
    {
        public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> enumerableList)
        {
            if (enumerableList != null)
            {
                //create an emtpy observable collection object
                var observableCollection = new ObservableCollection<T>();

                //loop through all the records and add to observable collection object
                foreach (var item in enumerableList)
                    observableCollection.Add(item);

                //return the populated observable collection
                return observableCollection;
            }
            return null;
        }
    }

wünsche dir viel Spass damit!

M
m.grauber Themenstarter:in
343 Beiträge seit 2010
vor 13 Jahren

Hallo Erfinder des Rades und Tom Leech!

das ist ja sehr eigentümlich. weil weiter oben sagtest du,
KundeListe = query.ToList();
täte nicht klappen (die Angabe des generischen typen <oDisp> ist übrigens entbehrlich)

Ja du hast Recht. Ich hatte inzwischen den gesamten Programmcode noch weiter geändert. Sicher hast du auch mit den Grundlagen Recht - ich habe schon in einigen Büchern gelesen aber durch die hohe Seitenzahl vergisst man wieder viel und einiges wird und kann auch nicht so gut erklärt werden und ist nur durch die Praxis vermittelbar.

Vielen Dank auch für den Code, die Daten in eine ObservableCollection zu bringen. Momentan benötige ich die ObservableCollection noch nicht zwingend. Da die zusätzlichen Schritte Zeit kosten, werde ich nun das Ergebnis vorerst in der Liste belassen.

Nochmals vielen Dank an Euch 👍 =) 👍 - damit kann ich das Thema schließen.

Grüße

Mfg
Michael

PS: Ich stelle nur Fragen, wenn ich in Büchern, im Web und in Foren nichts gefunden habe. Dumme Fragen bitte ich zu entschuldigen!

:] VISUAL STUDIO 2017 + .NET FRAMEWORK 4.5 + SQL-Server 2012 :]

16.842 Beiträge seit 2008
vor 13 Jahren

(ich sage es gelegentlich, und werd manchmal angemeckert, weil ich anonyme typen ziemlich nutzlos finde, weil man sie nur lokal verwenden kann - was will man mit daten, die man nicht weiter geben kann?)

Deswegen empfiehlt Microsoft mit dem Repository-Pattern und IQueryable<T> zu arbeiten.
Zusätzlich ist es ganz praktisch auf LinqTo (wenn möglich) zu verzichten und stattdessen Lamdba-Befehle zu nutzen, da die (eigentlich) immer typisierte Werte zurückgeben und in der Regel schneller sind.

Nach den MS-Empfehlungen würde es dann so aussehen:


//Lambda
IQueryable<ItemType> itemSet = db.ItemSet.Where( c => c.Name.Equals( name, StringComparison.CurrentCultureIgnoreCase ) ).Select( c => c );

//LinqTo
var itemSet = (from item in db.Itemset
				where item.Name.Equals( name, StringComparison.CurrentCultureIgnoreCase )
				select item)

Ich hab bisher noch nie einen Fall gehabt, bei dem IQueryable nicht funktionieren würde.

Grüße