แบบสอบถาม LINQ บน DataTable


1031

ฉันพยายามที่จะทำการสืบค้น LINQ บนวัตถุ DataTable และน่าประหลาดใจฉันพบว่าการดำเนินการแบบสอบถามดังกล่าวบน DataTables ไม่ตรงไปตรงมา ตัวอย่างเช่น:

var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;

สิ่งนี้ไม่ได้รับอนุญาต ฉันจะได้รับบางสิ่งเช่นนี้ได้อย่างไร

ฉันประหลาดใจที่ไม่อนุญาตให้ใช้คำสั่ง LINQ ใน DataTables!


3
คุณสามารถหาตัวอย่าง LINQ / แลมบ์ดาเพิ่มเติมได้จาก webmingle.blogspot.com/2010_09_01_archive.html

คำตอบ:


1279

คุณไม่สามารถแบบสอบถามกับDataTable's แถวคอลเลกชันเนื่องจากไม่ใช้DataRowCollection IEnumerable<T>คุณจำเป็นต้องใช้ส่วนขยายสำหรับAsEnumerable() DataTableชอบมาก

var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;

และตามที่@Keithพูดว่าคุณจะต้องเพิ่มการอ้างอิงไปยังSystem.Data.DataSetExtensions

AsEnumerable()IEnumerable<DataRow>ผลตอบแทน หากคุณต้องการแปลงIEnumerable<DataRow>เป็น a DataTableให้ใช้CopyToDataTable()ส่วนขยาย

ด้านล่างนี้คือข้อความค้นหาด้วย Lambda Expression

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);

8
รุ่น VB: ผลสลัว = จาก myRow ใน myDataTable.AsEnumerable _ โดยที่ myRow.Field ("RowNo") = 1 _ เลือก myRow
Jeff

15
ฉันมีการอ้างอิงถึง dll ที่กล่าวถึง แต่หายไปแล้วusing System.Data;
Luke Duddridge

5
VB Version จำเป็นต้องแทรก (Of String) ระหว่าง myRow.Field และ ("RowNo") ส่วนนั้นควรอ่าน: myRow.Field (Of String) ("RowNo") = 1 - การอ้างอิงความคิดเห็น @Cros
yougotiger

8
วิธีนี้มีความซับซ้อนโดยไม่จำเป็น ใช้myDataTable.Rowsแทนตามที่แนะนำ @JoelFan
สมรู้ร่วมคิด

10
@Markus เพียงชี้แจงเหตุผลที่ @ วิธีการแก้ปัญหาของ JoelFan ทำงานร่วมกับmyDataTable.Rowsเป็นเพราะตัวแปรถูกโยนอย่างชัดเจนmyRow เมื่อได้มีการรวบรวมแบบสอบถามที่ถูกเขียนใหม่เพื่อDataRow myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1)ส่วนตัวผมไม่พบการเรียกร้องให้AsEnumerable()ใด ๆ Cast<DataRow>()ที่ซับซ้อนมากขึ้นกว่าการเรียกร้องให้ เท่าที่ฉันรู้การแสดงก็เหมือนกันดังนั้นมันจึงเป็นเรื่องของความชอบ
Collin K

129
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow

2
แล้วการเลือกหลายแถวแทนที่จะเป็นแค่แถวที่ 1 ล่ะ?
Adjit

2
เพียงแค่ลบบรรทัด "ที่ไหน" และคุณจะได้รับแถวทั้งหมด
JoelFan

1
ใช่นี่คือวิธีที่ฉันใช้ทำยกเว้นการแทนที่(int)myRow["RowNo"]ด้วยแบบฟอร์มทั่วไปmyRow.Field<int>("RowNo")เพื่อสนับสนุนประเภท nullable ได้สะดวกยิ่งขึ้น
Jonas

69

ไม่ใช่ว่าพวกเขาจงใจไม่ได้รับอนุญาตบน DataTables มันเป็นเพียงแค่ DataTables ที่สร้าง IQneryable และ IEnumerable builds ล่วงหน้าซึ่งแบบสอบถาม Linq สามารถดำเนินการได้

อินเตอร์เฟสทั้งสองต้องการการตรวจสอบความปลอดภัยประเภทการเรียงลำดับ DataTables ไม่ได้พิมพ์อย่างยิ่ง นี่คือเหตุผลเดียวกันที่ทำให้ผู้คนไม่สามารถค้นหา ArrayList ได้เช่นกัน

เพื่อให้ Linq ทำงานคุณต้องแมปผลลัพธ์ของคุณกับวัตถุที่ปลอดภัยต่อประเภทและสืบค้นเทียบกับที่


49

As @ ch00k กล่าวว่า:

using System.Data; //needed for the extension methods to work

...

var results = 
    from myRow in myDataTable.Rows 
    where myRow.Field<int>("RowNo") == 1 
    select myRow; //select the thing you want, not the collection

คุณต้องเพิ่มการอ้างอิงโครงการด้วย System.Data.DataSetExtensions


1
ถ้าคุณพยายามที่นี้คุณจะพบว่ามันจะไม่ทำงานจนกว่าคุณใส่ประเภทที่เฉพาะเจาะจงในmyRowการใช้งานได้ที่Cast<DataRow>() ดีกว่าที่จะใช้Rows AsEnumerable()
NetMage

1
@NetMage ใช้งานได้เมื่อ 12 ปีก่อนเมื่อฉันโพสต์ ตราบใดที่คุณมีSystem.LinqและSystem.Data.DataSetExtensionsแล้วmyDataTable.Rowsผลตอบแทนที่คอลเลกชันของนับDataRowล่ะค่ะ นั่นอาจมีการเปลี่ยนแปลงมันเป็นเวลาสิบปีแล้วที่ฉันใช้มัน
คี ธ

1
น่าสนใจ - ฉันคิดว่ามันเปลี่ยนไปในบางจุดเนื่องจากมันไม่ทำงานบน. Net หรือ. Net Core ตอนนี้
NetMage

1
@NetMage ใช่ฉันไม่แปลกใจที่DataSetส่วนขยายไม่ได้ทำให้เป็น. NET Core หรือ. NET มาตรฐานพวกเขาล้าสมัยไปแล้วเมื่อฉันโพสต์คำตอบนี้ ฉันจะไม่ใช้DataSetในโครงการใหม่จริงๆมีรูปแบบการเข้าถึงข้อมูลที่ดีกว่าทั้งเพื่อความสะดวกในการเขียนโปรแกรมและประสิทธิภาพ
Keith

1
พวกเขาอยู่ที่นั่น แต่DataRowCollectionไม่ได้ใช้IEnumerable<T>เพียงIEnumerableและไม่ได้ทำงานกับ LINQ ที่พิมพ์
NetMage

38
var query = from p in dt.AsEnumerable()
                    where p.Field<string>("code") == this.txtCat.Text
                    select new
                    {
                        name = p.Field<string>("name"),
                        age= p.Field<int>("age")                         
                    };

เขตข้อมูลชื่อและอายุตอนนี้เป็นส่วนหนึ่งของวัตถุแบบสอบถามและสามารถเข้าถึงได้เช่น: Console.WriteLine (query.name);


ฉันจะใช้ชื่อได้อย่างไร ตัวอย่างเช่นMessageBox.Show(name)ไม่ได้กำหนด

35

ฉันรู้ว่าคำตอบนี้ได้รับการตอบสองสามครั้ง แต่เพื่อเสนอแนวทางอื่น:

ฉันชอบที่จะใช้.Cast<T>()วิธีนี้มันช่วยให้ฉันรักษาสติในการมองเห็นชนิดที่ชัดเจนและลึกลงไปฉันคิดว่า.AsEnumerable()มันเรียกว่า:

var results = from myRow in myDataTable.Rows.Cast<DataRow>() 
                  where myRow.Field<int>("RowNo") == 1 select myRow;

หรือ

var results = myDataTable.Rows.Cast<DataRow>()
                      .FirstOrDefault(x => x.Field<int>("RowNo") == 1);

ตามที่ระบุไว้ในความคิดเห็นไม่จำเป็นต้องมีชุดประกอบอื่น ๆ เนื่องจากเป็นส่วนหนึ่งของ Linq ( ข้อมูลอ้างอิง )


5
สิ่งนี้ทำงานโดยไม่อ้างอิง System.Data.DataSetExtensions
user423430

29

การใช้ LINQ เพื่อจัดการข้อมูลใน DataSet / DataTable

var results = from myRow in tblCurrentStock.AsEnumerable()
              where myRow.Field<string>("item_name").ToUpper().StartsWith(tbSearchItem.Text.ToUpper())
              select myRow;
DataView view = results.AsDataView();

1
AsDataView ไม่ปรากฏใน Intellisense ให้ฉัน ฉันรวมใช้ System.Data.Linq และใช้ System.Linq แต่ก็ยังไม่ทำงาน คุณรู้หรือไม่ว่าฉันกำลังคิดถึงอะไร? ขอบคุณล่วงหน้า.
นาโอมิ

@Naomi System.Data.DataSetExtensionsมันมาจาก
Louis Waweru

29
//Create DataTable 
DataTable dt= new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
   new DataColumn("ID",typeof(System.Int32)),
   new DataColumn("Name",typeof(System.String))

});

//Fill with data

dt.Rows.Add(new Object[]{1,"Test1"});
dt.Rows.Add(new Object[]{2,"Test2"});

//Now  Query DataTable with linq
//To work with linq it should required our source implement IEnumerable interface.
//But DataTable not Implement IEnumerable interface
//So we call DataTable Extension method  i.e AsEnumerable() this will return EnumerableRowCollection<DataRow>


// Now Query DataTable to find Row whoes ID=1

DataRow drow = dt.AsEnumerable().Where(p=>p.Field<Int32>(0)==1).FirstOrDefault();
 // 

22

ลองใช้บรรทัดคำค้นหาง่ายๆนี้:

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);

4
ฉันชอบ " วิธีการผูกมัด " (ตามที่คุณทำที่นี่) มากกว่า " ไวยากรณ์แบบสอบถาม " (ในคำตอบที่ยอมรับ) เพียงเพราะนี่เป็นพื้นฐานที่ประโยคที่เหมาะกับหนึ่งบรรทัดและยังอ่านง่ายมาก เพื่อให้แต่ละคนนั้นเอง
MikeTeeVee

16

คุณสามารถใช้ LINQ กับวัตถุในคอลเลกชันแถวเช่น:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;

1
เนื่องจากDataTable.Rowsไม่ได้ใช้งานIEnumerableฉันไม่สามารถดูว่าแบบสอบถามนี้สามารถรวบรวมได้อย่างไร
oneday เมื่อ

@oneday เมื่อฉันเพิ่งเห็นสิ่งนี้ถูกทำในบางรหัสและมันจะรวบรวม พยายามคิดดูว่าทำไมตอนนี้
BVernon

... หรือคุณสามารถใช้นิพจน์ตัวกรองภายในเมธอด Select: var results = myDataTable.Select ("RowNo = 1"); ส่งคืนอาร์เรย์ DataRow
Ishikawa

12

นี่เป็นวิธีง่ายๆที่เหมาะกับฉันและใช้การแสดงออกแลมบ์ดา:

var results = myDataTable.Select("").FirstOrDefault(x => (int)x["RowNo"] == 1)

ถ้าคุณต้องการค่าเฉพาะ:

if(results != null) 
    var foo = results["ColName"].ToString()


11

เป็นไปได้มากว่าคลาสสำหรับชุดข้อมูล, DataTable และ DataRow จะถูกกำหนดไว้แล้วในโซลูชัน หากเป็นกรณีนี้คุณไม่จำเป็นต้องมีการอ้างอิง DataSetExtensions

อดีต ชุดข้อมูลชื่อคลาส -> กำหนดเอง, DataRow คลาสชื่อ -> CustomTableRow (พร้อมคอลัมน์ที่กำหนด: RowNo, ... )

var result = from myRow in myDataTable.Rows.OfType<CustomSet.CustomTableRow>()
             where myRow.RowNo == 1
             select myRow;

หรือ (ตามที่ฉันต้องการ)

var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);


8

ในใบสมัครของฉันฉันพบว่าการใช้ LINQ ไปยังชุดข้อมูลที่มีส่วนขยาย AsEnumerable () สำหรับ DataTable ตามที่แนะนำในคำตอบนั้นช้ามาก หากคุณสนใจที่จะปรับความเร็วให้ใช้ห้องสมุด Json.Net ของ James Newtonking ( http://james.newtonking.com/json/help/index.html )

// Serialize the DataTable to a json string
string serializedTable = JsonConvert.SerializeObject(myDataTable);    
Jarray dataRows = Jarray.Parse(serializedTable);

// Run the LINQ query
List<JToken> results = (from row in dataRows
                    where (int) row["ans_key"] == 42
                    select row).ToList();

// If you need the results to be in a DataTable
string jsonResults = JsonConvert.SerializeObject(results);
DataTable resultsTable = JsonConvert.DeserializeObject<DataTable>(jsonResults);

ฉันสงสัยว่านี่จะเร็วกว่าในกรณีทั่วไป มันมีค่าใช้จ่ายในการทำให้เป็นอันดับสองการดีซีเรียลไลเซชันหนึ่งและการแยกวิเคราะห์ โดยไม่คำนึงถึงฉัน downvote เพราะมันไม่รัดกุมเช่น serialization / deserialization ไม่ชัดเจนว่าเจตนาคือการกรองรายการ
phu

@an phu โดยใช้วิธีการขยาย. AsEnumerable สร้างชุดของSystem.Data.DataRowวัตถุหนา ตารางข้อมูลแบบอนุกรมและแยกวิเคราะห์สร้างข้อมูลที่มีน้ำหนักเบาซึ่งประกอบด้วยชื่อคอลัมน์และค่าของแต่ละแถวเท่านั้น เมื่อแบบสอบถามทำงานมันจะโหลดข้อมูลไปยังหน่วยความจำซึ่งสำหรับชุดข้อมูลขนาดใหญ่อาจเกี่ยวข้องกับการแลกเปลี่ยน บางครั้งค่าใช้จ่ายในการดำเนินการหลายครั้งน้อยกว่าค่าใช้จ่ายในการคัดลอกข้อมูลจำนวนมากทั้งในและนอกหน่วยความจำ
LandedGently

7

สำหรับ VB.NET รหัสจะมีลักษณะดังนี้:

Dim results = From myRow In myDataTable  
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow


7

ตัวอย่างเกี่ยวกับวิธีการบรรลุสิ่งที่ให้ไว้ด้านล่าง:

DataSet dataSet = new DataSet(); //Create a dataset
dataSet = _DataEntryDataLayer.ReadResults(); //Call to the dataLayer to return the data

//LINQ query on a DataTable
var dataList = dataSet.Tables["DataTable"]
              .AsEnumerable()
              .Select(i => new
              {
                 ID = i["ID"],
                 Name = i["Name"]
               }).ToList();

6

ลองสิ่งนี้ ...

SqlCommand cmd = new SqlCommand( "Select * from Employee",con);
SqlDataReader dr = cmd.ExecuteReader( );
DataTable dt = new DataTable( "Employee" );
dt.Load( dr );
var Data = dt.AsEnumerable( );
var names = from emp in Data select emp.Field<String>( dt.Columns[1] );
foreach( var name in names )
{
    Console.WriteLine( name );
}

5

คุณสามารถทำให้มันทำงานได้อย่างสง่างามผ่าน linq ดังนี้:

from prod in TenMostExpensiveProducts().Tables[0].AsEnumerable()
where prod.Field<decimal>("UnitPrice") > 62.500M
select prod

หรือเช่นไดนามิก linq นี้ (AsDynamic เรียกว่าโดยตรงบนชุดข้อมูล):

TenMostExpensiveProducts().AsDynamic().Where (x => x.UnitPrice > 62.500M)

ฉันชอบวิธีสุดท้ายในขณะที่ยืดหยุ่นที่สุด PS: อย่าลืมเชื่อมต่อSystem.Data.DataSetExtensions.dllการอ้างอิง


5

คุณสามารถลองได้ แต่คุณต้องแน่ใจว่าประเภทของค่าสำหรับแต่ละคอลัมน์

List<MyClass> result = myDataTable.AsEnumerable().Select(x=> new MyClass(){
     Property1 = (string)x.Field<string>("ColumnName1"),
     Property2 = (int)x.Field<int>("ColumnName2"),
     Property3 = (bool)x.Field<bool>("ColumnName3"),    
});

โลกบ้าไปแล้วเหรอ? เกิดอะไรขึ้นกับ sql DataRow [] drs = dt.Select ("id = 1"); บางทีนี่อาจจะง่ายเกินไป
Programnik

0

ฉันเสนอวิธีแก้ปัญหาต่อไปนี้:

DataView view = new DataView(myDataTable); 
view.RowFilter = "RowNo = 1";
DataTable results = view.ToTable(true);

ดูDataView Documentationสิ่งแรกที่เราเห็นคือ:

แสดงถึงมุมมอง databindable ที่กำหนดเองของ DataTable สำหรับการเรียงลำดับการกรองการค้นหาการแก้ไขและการนำทาง

สิ่งที่ฉันได้รับจากสิ่งนี้คือ DataTable มีไว้เพื่อเก็บข้อมูลเท่านั้นและ DataView นั้นช่วยให้เราสามารถ "ค้นหา" กับ DataTable ได้

นี่คือวิธีการทำงานในกรณีนี้โดยเฉพาะ:

คุณพยายามที่จะใช้คำสั่ง SQL

SELECT *
FROM myDataTable
WHERE RowNo = 1

ใน "ภาษา DataTable" ใน C # เราจะอ่านแบบนี้:

FROM myDataTable
WHERE RowNo = 1
SELECT *

ซึ่งมีลักษณะเป็น C # ดังนี้:

DataView view = new DataView(myDataTable);  //FROM myDataTable
view.RowFilter = "RowNo = 1";  //WHERE RowNo = 1
DataTable results = view.ToTable(true);  //SELECT *

0
                    //Json Formating code
                    //DT is DataTable
                    var filter = (from r1 in DT.AsEnumerable()

                                  //Grouping by multiple columns 
                                  group r1 by new
                                  {
                                      EMPID = r1.Field<string>("EMPID"),
                                      EMPNAME = r1.Field<string>("EMPNAME"),

                                  } into g
                                  //Selecting as new type
                                  select new
                                  {

                                      EMPID = g.Key.EMPID,
                                      MiddleName = g.Key.EMPNAME});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.