Autor Beitrag
Marc Dirk
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Di 23.03.10 23:46 
Hallo,

ich habe folgendes Problem:
Ich beziehe von meinem Lieferanten für meinen Onlineshop täglich seine komplette Artikeldatenbank als CSV-File (55MB).
Meine Warenwirtschaft importiert CSV-Datein allerdings in einem anderen Format (andere Separatoren z.B. ";" statt ",", andere Spaltennamen und diverse Spalten zuviel und andere dafür zuwenig.
Bisher habe ich diese CSV-Files in Excel importiert, manuell umgebaut, sortiert und ergänzt und dann wieder als CSV-Datei abgespeichert. Das dauert Stunden.

Um mir das Leben zu erleichtern habe ich angefangen C# zu lernen (macht mächtig Spaß). Ich möchte ein Programm schreiben, dass diese CSV-Datei automatisch filtert und konvertiert.
Meine jetzigen Programmierkünste reichen bisher aus um ein DataTable mit den Daten aus der CSC-Datei zu befüllen und im DataGridView anzuzeigen.

Als erstes möchte ich nun das DataTable um die Daten (Artikel) erleichtern die ich sowieso nicht im Shop haben möchte.
Wie kann ich nun in der DataTable programmgesteuert gezielt die Zeilen finden die in der Spalte in der ich suche nicht die richtigen Schlüsselworter hat.
z.B.

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
Art.-Nr.  Art.-name  Warengruppe
1    Artikelname1  Schuhe
2    Artikelname2  Schuhe          
3    Artikelname3  Jacken 
4    Artikelname4  Hosen 
5    Artikelname5  Schuhe 
6    Artikelname6  Jacken 
7    Artikelname7  Anzüge 
8    Artikelname8  Hüte 
9    Artikelname9  Hosen


Ich möchte nun das alles, außer den Zeilen die in der Spalte Warengruppe Schuhe und Hosen stehen haben, gelöscht wird, so das das neue DataTable so aussieht:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
Art.-Nr.  Art.-name  Warengruppe
1    Artikelname1  Schuhe
2    Artikelname2  Schuhe          
4    Artikelname4  Hosen 
5    Artikelname5  Schuhe 
9    Artikelname9  Hosen


Danach muß die Tabelle um Spalten ergänzt und befüllt werden und andere Spalten müssen gelöscht werden.
Und dann kommt die eigentliche Aufgabe. Ich möchte aus der ebenso bearbeiteten Tabelle des Vortages die Zeilen finden die sich geändert haben bzw. der alten Vortags-Tabelle noch gar nicht enthalten waren (es kommen fast täglich neue Artikel dazu). Alle Zeilen die in beiden Tabellen dann gleich sind können nun auch gelöscht werden.
Die so resultierende dritte "Differenzdaten-Tabelle" ist nun so klein wie möglich und soll dann wieder als CSV gespeichert werden.

Wenn ich die Tabelle nicht um den unnützen Ballast befreie dann dauert der Import in meine WaWi bei 60.000 Artikeln viele Stunden!

Ich bin nun seit einer Woche an dem Ganzen drann und verzweifel nun langsam aber sicher, da ich im Web kaum etwas hilfreiches hierzu gefunden habe. :cry:
:wink: Welcher nette Zeitgenosse kann mir hierbei gute Tipps oder besser noch Code-Beispiele geben um die oben genannten Teilaufgaben zu lösen?

Viele Grüße
Marc

Moderiert von user profile iconKha: Code-Tags hinzugefügt
danielf
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 1012
Erhaltene Danke: 24

Windows XP
C#, Visual Studio
BeitragVerfasst: Mi 24.03.10 00:17 
Hallo und :welcome:,

bei .NET gibt es die Online-Hilfe MSDN. Dort findest du viele Beispiel zu relevanten Themen. Zum Beispiel Filtern von DataTable.

Damit kannst du bequem dein Anliegen umsetzen.

Viel Spaß noch und Gruß
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mi 24.03.10 00:57 
:welcome: !

Die von user profile icondanielf erwähnten Filter sind ganz nett, aber bei komplexeren oder gar dynamischen Abfragen würde ich lieber selbst filtern. Bei einer List<string[]> (ich sehe bis jetzt keinen Grund, die komplexere DataTable zu benutzen) könnte das so aussehen:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
HashSet<string> approvedGroups = new HashSet<string> { "Schuhe""Hosen" };

bool IsApproved(string[] item)
{
  return approvedGroups.Contains(item[2]);
}

List<string[]> filtered = items.FindAll(IsApproved);

So, extra mal auf anonyme Methoden verzichtet ;) . Kann aber natürlich gut sein, dass das trotzdem noch ein wenig zu viel für dich ist - in diesem Falle würde auch eine for-Schleife rückwärts über die Zeilen der DataTable genügen, in der du dann ggf. RemoveAt aufrufst.

Um zu dem wesentlich interessanteren Problem der Diff-Tabelle zu kommen (wieder auf List<string[]>, würde so aber auch mit DataTable funktionieren)... LINQ:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
// Kann sich die Warengruppe auch ändern?
class MyItemComparer : IEqualityComparer<DataTable>
{
  public bool Equals(string[] item1, string[] item2)
  {
    return item1[0] == item2[0] && item1[1] == item2[1];
  }

  public int GetHashCode(string[] item)
  {
    return item[0].GetHashCode() ^ item[1].GetHashCode();
  }
}

List<string[]> diff = itemsToday.Except(itemsYesterday, new MyItemComparer()).ToList(); // Ba Da Bing!

Wieder gäbe es natürlich umständlichere, aber einfacher zu erklärende Möglichkeiten. Aber ich warte lieber erst einmal auf dein genaues Feedback :) .

_________________
>λ=
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Mi 24.03.10 01:47 
Ich werfe mal das hier noch in den Raum:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
//Holt alle Artikel in eine generische Auflistung die Bezeichnung Hose oder Schuhe beinhalten

            IEnumerable<DataRow> ArtikelInDerNeuenTabelle =
                from DataRow Artikel in NeueTabelle.Rows
                where
                        ((string)Artikel["Warengruppe"]).Contains("Hose") ||
                        ((string)Artikel["Warengruppe"]).Contains("Schuhe")
                select Artikel;

//Holt alle Spalten, die nicht in der alten Tabelle sind oder sich geändert haben.
//Der PrimaryKey von AlteTabelle muss dafür alle Spalten enthalten. Das kann man
//beliebig anpassen und z.B. nur bestimmte Spalten nach Änderung durchsuchen

            IEnumerable<DataRow> NeueArtikel =
                from DataRow Artikel in ArtikelInDerNeuenTabelle
                where !(AlteTabelle.Rows.Contains(Artikel.ItemArray))
                select Artikel;

//Befüllt eine neue Tabelle mit den geänderten Artikeln
            foreach (DataRow dr in NeueArtikel)
            {
                ResultierendeTabelle.ImportRow(dr);
            }


Alle Tabellen sollten mit den gleichen Column-Einträgen erstellt werden. Zumindest muss die resultierende Tabelle die Colums-Einträge der neuen Tabelle enthalten. Es kommt zwar zu keinem Fehler wenn nicht, aber die Daten sind sonst nicht auswertbar.

:-)
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Mi 24.03.10 12:15 
user profile icondanielf hat folgendes geschrieben Zum zitierten Posting springen:
Hallo und :welcome:,

bei .NET gibt es die Online-Hilfe MSDN. Dort findest du viele Beispiel zu relevanten Themen. Zum Beispiel Filtern von DataTable.

Damit kannst du bequem dein Anliegen umsetzen.

Viel Spaß noch und Gruß





Danke für den Hinweis! Dort habe ich eine Menge Lesestoff.
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 00:37 
Hallo norman2306!

Vielen Dank für Deine Hilfe. Deine Vorschläge haben mich recht weit gebracht.
Leider kämpfe ich mit einem Fehler rum (Das Objekt des Typs "System.DBNull" kann nicht in Typ "System.String" umgewandelt werden.) der nur Auftritt weil in einigen Zellen der zu filternden Spalte nichts drin steht. Kann man diesen Fehler irgendwie im Code abfangen. Ist der Rest meines Code so o.K., oder kann er weiter optimiert werden, damit er schneller wird?
Ich bin für jeden Vorschlag offen!

Euch allen noch mal ganz herzlichen Dank für Eure Unterstützung!

Hier mein Code!

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
        // Das übergebene DataTable wird mit den Schlüsselwörtern in dem 
        // ebenfalls übergebenen List Objekt gefiltert. Der Filter 
        // wirkt hierbei auf die übergebene Spalte. Das Rückgabe Objekt
        // ist eine DataTable die um all die unerwünschten Zeilen erleichtert 
        // wurde.
        private DataTable GetfilteredDataTable(DataTable dt, List<string> filter, int row)
        {
           IEnumerable<DataRow> ArtikelInDerNeuenTabelle =
                from DataRow Artikel in dt.Rows
                where ((string)Artikel[row]).Contains(filter[0].ToString()) || 
                      ((string)Artikel[row]).Contains(filter[1].ToString())
                select Artikel;
            DataTable boundnewdt = ArtikelInDerNeuenTabelle.CopyToDataTable<DataRow>();
            
            return boundnewdt;
        }


Moderiert von user profile iconKha: C#-Tags hinzugefügt
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Do 25.03.10 01:02 
Dafür kannst du LINQ to DataSets benutzen: DataRowExtensions.Field liefert statt DBNull ein "gewöhnliches" null zurück.
ausblenden C#-Quelltext
1:
var artikelInDerNeuenTabelle = dt.Rows.Where(artikel => filter.Contains(artikel.Field<string>(row));					


user profile iconMarc Dirk hat folgendes geschrieben Zum zitierten Posting springen:
Ist der Rest meines Code so o.K., oder kann er weiter optimiert werden, damit er schneller wird?
Ich bin für jeden Vorschlag offen!
Ich hatte da zum Beispiel so einen Vorschlag: HashSet<T> könnte noch etwas schneller sein, wenn es etwas mehr als zwei Filter-Kategorien sind/werden.

_________________
>λ=
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Do 25.03.10 01:07 
Freut mich, das es dir weiter geholfen hat :)

Hier mal ein Tip, wie du den Fehler umschiffen kannst
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
        private DataTable GetfilteredDataTable(DataTable dt, List<string> filter, int row)
        {
           IEnumerable<DataRow> ArtikelInDerNeuenTabelle =
                from DataRow Artikel in dt.Rows
                where Artikel[row] != System.DBNull && (
                      ((string)Artikel[row]).Contains(filter[0].ToString()) || 
                      ((string)Artikel[row]).Contains(filter[1].ToString()))
                select Artikel;
            DataTable boundnewdt = ArtikelInDerNeuenTabelle.CopyToDataTable<DataRow>();
            
            return boundnewdt;
        }


Um es schneller zu machen, müsstest du Khas vorschlag mit dem HashSet nutzen.
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Do 25.03.10 01:08 
Oh, da hatter er schon selber geschrieben :)
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 02:08 
Hallo Norman,

ich bekomme mit Deinem Vorschlag leider den folgenden Fehler "System.DBNull" ist "Typ" und im angegebenen Kontext nicht gültig.
Was kann ich tun? :cry:

Was sind HashSet<T> für Objekte und wie arbeitet man mit ihnen? :?:
Kann jemand meine Methode so umschreiben, dass sie mit HashSet<T> funktioniert?

Danke für Eure Hilfe!

Gruß

Marc
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Do 25.03.10 08:12 
Servus... war schon im Bett. Habe gerade kein Compiler, deswegen hatte ich es nicht getestet. Sorry, da hatte ich nicht aufgepasst. Dann versuch es mit dem "is" Schlüsselwort. Alternativ ginge auch "typeof". Am besten du testet, ob es als string interpretiert werden kann. Das ist eh besser als nur DBNull auszugrenzen:

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
       private DataTable GetfilteredDataTable(DataTable dt, List<string> filter, int row)
        {
           IEnumerable<DataRow> ArtikelInDerNeuenTabelle =
                from DataRow Artikel in dt.Rows
                where Artikel[row] is string && (
                      ((string)Artikel[row]).Contains(filter[0].ToString()) || 
                      ((string)Artikel[row]).Contains(filter[1].ToString()))
                select Artikel;
            DataTable boundnewdt = ArtikelInDerNeuenTabelle.CopyToDataTable<DataRow>();
            
            return boundnewdt;
        }


Oder mit:
ausblenden C#-Quelltext
1:
Artikel[row].GetType() == typeof(string)					

Oder
ausblenden C#-Quelltext
1:
!(Artikel[row] is System.DBNull)					




Ist nicht getestet, das musst du machen.
Mit den Hashset, das wäre jetzt etwas aufwendiger zu erklären, da habe ich leider keine Zeit, muss auf arbeit. Ich schau aber mal da, ob ich Zeit finde.
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 10:27 
Super Danke!!!!!

"where Artikel[row] is string && (" hat einwandfrei funktioniert!

Jetzt muß ich nur noch rausbekommen wie ich den ganzen Inhalt von filter abgefragt bekomme, egal wie lang die Liste ist.

Danke!
Marc
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Do 25.03.10 10:37 
So jetzt:

Statt in ein DataTable zu importieren, importierst du dein csv in ein IEnumerable aus string[]. Das String-Array stellt dabei jeweils eine Zeile(Row) deiner Tabelle dar. Für jede Zeile fügst du der Liste ein neues String-Array hinzu. Somit generierst du eine eigene Tabelle, mit der du aber keine Typ-Konversation durchführen musst.

Dann nutzt du den Code den Kha gepostet hat, oder den, den ich gepostet habe. Welcher von den beiden schneller ist, musst du testen. Im Prinzip sparst du dir dadurch die Typkonversation und -überprüfung. Außerdem die ganzen DataBinding anfragen, Überprüfungen etc. die im DataGrid ablaufen. Damit dürftest du dann gut 30% schneller sein.

Hier mal ein Beispiel:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
namesspace MyApp
{
    public static class AddOnMethods
    {
        /// <summary>
        /// Überprüft, ob der String einen der gesuchten Strings enthält
        /// </summary>
        /// <param name="item"></param>
        /// <param name="searchStrings">Die Strings, nach denen gesucht werden soll</param>
        /// <returns></returns>
        public static bool ContainsAny(this string item, string[] searchStrings)
        {
            bool res = false;
            foreach (string ss in searchStrings)
            {
                res |= item.Contains(ss);
            }

            return res;
        }
    }

    class DeineMainClass
    {
        private IEnumerable<string[]> GetfilteredDataTable(IEnumerable<string[]> dt, string[] filter, int row)
        {
            return
                    from string[] Artikel in dt
                    where Artikel[row].ContainsAny(filter)
                    select Artikel;      
        }
    }
}
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 11:03 
Danke!
Ich habe jetzt noch einen Termin, werde danach aber versuchen Deinen Code zu implementieren, so fern ich ihn verstehe.
Gruß
Marc
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 16:10 
Hallo Kha,

auch Dir vielen Dank für Deinen Vorschlag.
Die Mischung aus dem Code von Dir und von Norman hat dann endlich den Durchbruch gebracht und er ist auch ausreichend schnell, so das ich im Augenblick nicht auf das HashSet<T> umsteigen werde.

Den Rest des Projektes werde ich dann auch noch posten wenn ich soweit bin.
Hier also der letztgültige Code für die erste Teillösung für alle die Ihn auch noch gebrauchen können.

Gruß
Marc

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
        // Das übergebene DataTable wird mit den Schlüsselwörtern in dem 
        // ebenfalls übergebenen List Objekt gefiltert. Der Filter 
        // wirkt hierbei auf die übergebene Spalte. Das Rückgabe Objekt
        // ist eine DataTable die um all die unerwünschten Zeilen erleichtert 
        // wurde.
        private DataTable GetfilteredDataTable(DataTable dt, List<string> filter, int col)
        {
            IEnumerable<DataRow> ArticleInNewDataTable =
                 from DataRow Article in dt.Rows
                 where Article[col] is string && (filter.Contains(Article.Field<string>(col)))
                 select Article;
            return ArticleInNewDataTable.CopyToDataTable<DataRow>();
        }


Moderiert von user profile iconKha: C#-Tags hinzugefügt


Zuletzt bearbeitet von Marc Dirk am Do 25.03.10 18:32, insgesamt 1-mal bearbeitet
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Do 25.03.10 17:44 
user profile iconMarc Dirk hat folgendes geschrieben Zum zitierten Posting springen:
auch Dir vielen Dank für Deinen Vorschlag.
Die Mischung aus dem Code von Dir und von Norman hat dann endlich den Durchbruch gebracht und er ist auch ausreichend schnell, so das ich im Augenblick nicht auf das HashSet<T> umsteigen werde.
Ja, das HashSet würde wahrscheinlich nicht viel ändern. Solange die Verarbeitung in O(n) geschieht, spielt sie im Vergleich zum Einlesen der Datei wahrscheinlich sowieso keine Rolle.
Und wie ich geschrieben habe: Über .Field umgehst du die gesamte DBNull-Problematik, das is ist damit unnötig. Du kannst die gesamte from-Query durch meinen Code ersetzen.

_________________
>λ=
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 18:43 
Hallo Kha,

ich habe Dein Beispiel mit ".Field" nicht kapiert. Dafür bin ich wohl noch zu grün hinter den Ohren.
Das bloße ersetzen der Query mit der einen Zeile aus Deinem Beispiel liefert logischerweise jede Menge Fehler zurück.
Ich vermag sie aber nicht entsprechend anzupassen.

Kannst Du meine Methode komplett so umschreiben wie Du es meinst und ein wenig kommentieren.
Ich hoffe das ich dann kapiere wie Du es gemeint hast.

Gruß

Marc
norman2306
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 222
Erhaltene Danke: 16

Win XP, Win 7 64-Bit
C# 4.0 (VS2010)
BeitragVerfasst: Do 25.03.10 19:01 
einfach nur:

ausblenden C#-Quelltext
1:
artikelInDerNeuenTabelle = dt.Rows.Where(artikel => filter.Contains(artikel.Field<string>(row));					


das from und select brauchst dann nicht mehr. Das wird durch den Lambda-Ausdruck (=>) ersetzt. Das ist so eine Art Delegate ohne den Delegate explizit zu definieren. Aber dann kann man während des Debuggens keine Codeänderungen mehr machen, da gibt es dann Fehlermeldung.
Marc Dirk Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 41



BeitragVerfasst: Do 25.03.10 19:17 
user profile iconnorman2306 hat folgendes geschrieben Zum zitierten Posting springen:
einfach nur:

ausblenden C#-Quelltext
1:
artikelInDerNeuenTabelle = dt.Rows.Where(artikel => filter.Contains(artikel.Field<string>(row));					


das from und select brauchst dann nicht mehr. Das wird durch den Lambda-Ausdruck (=>) ersetzt. Das ist so eine Art Delegate ohne den Delegate explizit zu definieren. Aber dann kann man während des Debuggens keine Codeänderungen mehr machen, da gibt es dann Fehlermeldung.



Aber bei mir klappt das nicht!

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
     private DataTable GetfilteredDataTable(DataTable dt, List<string> filter, int col)
        {
            var ArticleInNewDataTable = dt.Rows.where(Article => filter.Contains(Article.Field<string>(col)));
            return ArticleInNewDataTable.CopyToDataTable<DataRow>();
        }


Fehler "System.Data.DataRowCollection" enthält keine Definition für "where", und es konnte keine Erweiterungsmethode "where" gefunden werden, die ein erstes Argument vom Typ "System.Data.DataRowCollection" akzeptiert. (Fehlt eine Using-Direktive oder ein Assemblyverweis?)

What to do?????
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Do 25.03.10 20:46 
Diese verdammten untypisierten 1.1-Collections :< . Nimm dt.Rows.Cast<DataRow>().Where (Enumerable-Extension: IEnumerable -> IEnumerable<T>) oder in diesem speziellen Fall auch dt.AsEnumerable().Where (DataTableExtentions). Wusste gar nicht, dass Query Expressions mit einem expliziten Typ automatisch Cast() einfügen :zustimm: .

_________________
>λ=