การลบแถวเฉพาะจาก DataTable


87

ฉันต้องการลบบางแถวออกจาก DataTable แต่มีข้อผิดพลาดเช่นนี้

มีการแก้ไขคอลเล็กชัน การดำเนินการแจงนับอาจไม่ดำเนินการ

ฉันใช้สำหรับการลบรหัสนี้

foreach(DataRow dr in dtPerson.Rows){
    if(dr["name"].ToString()=="Joe")
        dr.Delete();
}

แล้วปัญหาคืออะไรและจะแก้ไขได้อย่างไร? คุณแนะนำวิธีใด

คำตอบ:


170

หากคุณลบรายการออกจากคอลเลกชั่นคอลเลกชั่นนั้นจะเปลี่ยนไปและคุณจะไม่สามารถระบุรายการต่อไปได้

ให้ใช้ For loop แทนเช่น:

for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
    DataRow dr = dtPerson.Rows[i];
    if (dr["name"] == "Joe")
        dr.Delete();
}
dtPerson.AcceptChanges();

โปรดทราบว่าคุณกำลังทำซ้ำแบบย้อนกลับเพื่อหลีกเลี่ยงการข้ามแถวหลังจากลบดัชนีปัจจุบัน


@Slugster เอาชนะฉันไป! (ผมเปลี่ยนของคุณ[ii]ไป[i]แต่ :-)
Widor

11
สิ่งนี้ไม่ถูกต้อง คุณสามารถใช้ foreach เพื่อวนรอบตารางในขณะที่ลบแถว ดูคำตอบโดยสตีฟ
Alexander Garden

3
คำตอบนี้ควรมีคำตอบของ @ bokkie ด้วย หากเราใช้ในDataTableภายหลังมันจะทำให้เกิดข้อยกเว้น วิธีที่ถูกต้องจะโทรRemove()กับแหล่งที่มา-DataTable dtPerson.Rows.Remove(dr)
Code.me

หากคุณใช้ DataTable เพื่ออัปเดตตารางในเซิร์ฟเวอร์ฐานข้อมูล @Steve มีคำตอบที่ดีกว่า คุณสามารถทำเครื่องหมายแถวว่าถูกลบอัปเดตแถวและเพิ่มแถวใหม่ทั้งหมดในลูปเดียว คุณสามารถใช้ SqlAdapter เพื่อยอมรับการเปลี่ยนแปลงกับตาราง Db เมื่อพิจารณาว่าปัญหาเกิดขึ้นบ่อยแค่ไหนกระบวนการทั้งหมดมีความซับซ้อนมากกว่าที่คุณคิด แต่ก็ใช้ได้ผล ถ้าฉันจะไม่ใช้ประโยชน์จากลักษณะการทำธุรกรรมของ DataTable ฉันแค่ใช้การรวบรวมวัตถุและวิธีการ Namco หรือ Widor
BH

ไม่Delete()ต้องใช้การโทรAcceptChanges()เพื่อให้การลบมีผล?
Broots Waymb

128

ก่อนที่ทุกคนจะกระโดดไปที่แบนด์วากอน ' คุณไม่สามารถลบแถวในการแจงนับ ' คุณต้องตระหนักก่อนว่า DataTables เป็นธุรกรรมและอย่าลบการเปลี่ยนแปลงในทางเทคนิคจนกว่าคุณจะเรียกAcceptChanges ()

หากคุณจะเห็นข้อยกเว้นนี้ในขณะที่โทรลบคุณจะอยู่ในระหว่างการพิจารณาเปลี่ยนแปลงข้อมูลรัฐ ตัวอย่างเช่นหากคุณเพิ่งโหลดจากฐานข้อมูลการเรียก Delete จะทำให้เกิดข้อยกเว้นหากคุณอยู่ใน foreach loop

แต่! แต่!

หากคุณโหลดแถวจากฐานข้อมูลและเรียกใช้ฟังก์ชัน ' AcceptChanges () ' คุณจะยอมรับการเปลี่ยนแปลงที่รอดำเนินการทั้งหมดใน DataTable ตอนนี้คุณสามารถวนซ้ำผ่านรายการแถวที่เรียก Delete () ได้โดยไม่ต้องสนใจโลกเพราะเพียงแค่ทำเครื่องหมายแถวสำหรับการลบ แต่จะไม่ถูกผูกมัดจนกว่าคุณจะเรียกAcceptChanges () อีกครั้ง

ฉันรู้ว่าคำตอบนี้ค่อนข้างล้าสมัย แต่ฉันต้องจัดการกับปัญหาที่คล้ายกันเมื่อเร็ว ๆ นี้และหวังว่าสิ่งนี้จะช่วยลดความเจ็บปวดสำหรับนักพัฒนาในอนาคตที่ทำงานกับโค้ดอายุ 10 ปี :)


Ps นี่คือตัวอย่างโค้ดง่ายๆที่เพิ่มโดยJeff :

ค#

YourDataTable.AcceptChanges(); 
foreach (DataRow row in YourDataTable.Rows) {
    // If this row is offensive then
    row.Delete();
} 
YourDataTable.AcceptChanges();

VB.Net

ds.Tables(0).AcceptChanges()
For Each row In ds.Tables(0).Rows
    ds.Tables(0).Rows(counter).Delete()
    counter += 1
Next
ds.Tables(0).AcceptChanges()

สำหรับเวอร์ชัน c # จำเป็นต้องใช้ {and} แทน ()
BugLover

2
ยังมีประโยชน์มากขึ้น (ฉันคิดว่า) เปลี่ยนobject row_loopVariable in ds.Tables(0).Rowsเป็นDataRow row in ds.Tables(0).Rows
BugLover

2
Ffs สิ่งนี้ช่วยฉันได้ในระหว่างการปรับใช้วันหยุดสุดสัปดาห์ฝันร้าย คุณสมควรได้รับเบียร์ทั้งหมด!
James Love

ดูเอกสารได้ที่msdn.microsoft.com/de-de/library/…
Andreas Krohn

รหัสที่ดี สิ่งหนึ่งใน C # วิธีทั่วไปที่จะเพิ่มขึ้นโดยหนึ่งเป็นแทนcounter++ counter+= 1
MQuiggGeorgia

18

ด้วยวิธีนี้:

for(int i = dtPerson.Rows.Count-1; i >= 0; i--) 
{ 
    DataRow dr = dtPerson.Rows[i]; 
    if (dr["name"] == "Joe")
        dr.Delete();
} 

หากคุณกำลังจะใช้ datatable หลังจากลบแถวคุณจะได้รับข้อผิดพลาด สิ่งที่คุณทำได้คือแทนที่dr.Delete();ด้วยdtPerson.Rows.Remove(dr);


16

สิ่งนี้ใช้ได้กับฉัน

List<string> lstRemoveColumns = new List<string>() { "ColValue1", "ColVal2", "ColValue3", "ColValue4" };
List<DataRow> rowsToDelete = new List<DataRow>();

foreach (DataRow row in dt.Rows) {
    if (lstRemoveColumns.Contains(row["ColumnName"].ToString())) {
        rowsToDelete.Add(row);
    }
}

foreach (DataRow row in rowsToDelete) {
    dt.Rows.Remove(row);
}

dt.AcceptChanges();

ง่ายมากที่จะพลาด dt.AcceptChanges ()
Matthew Lock

"คุณยังสามารถเรียกเมธอด Delete ของคลาส DataRow เพื่อเพียงแค่ทำเครื่องหมายแถวสำหรับการลบการเรียก Remove จะเหมือนกับการเรียก Delete จากนั้นเรียก AcceptChanges ไม่ควรเรียก Remove ใน foreach loop ในขณะที่วนซ้ำผ่านอ็อบเจ็กต์ DataRowCollection.Remove แก้ไขสถานะของคอลเล็กชัน " ดูmsdn.microsoft.com/de-de/library/… ไชโย
Andreas Krohn

9
DataRow[] dtr=dtPerson.select("name=Joe");
foreach(var drow in dtr)
{
   drow.delete();
}
dtperson.AcceptChanges();

ฉันหวังว่ามันจะช่วยคุณได้


1
คำสั่งdrow.Delete();ไม่ใช่drow.delete();วิธีการที่คำนึงถึงตัวพิมพ์เล็กและใหญ่ใน. net btw
MethodMan

5

หากต้องการลบทั้งแถวออกจากDataTableให้ทำเช่นนี้

DataTable dt = new DataTable();  //User DataTable
DataRow[] rows;
rows = dt.Select("UserName = 'KarthiK'");  //'UserName' is ColumnName
foreach (DataRow row in rows)
     dt.Rows.Remove(row);

4

หรือเพียงแค่แปลงคอลเลกชันแถวDataTable เป็นรายการ:

foreach(DataRow dr in dtPerson.Rows.ToList())
{
    if(dr["name"].ToString()=="Joe")
    dr.Delete();
}

1

ปัญหาอยู่ที่ไหน: ห้ามไม่ให้ลบรายการออกจากคอลเลกชันภายใน foreach loop

วิธีแก้ไข: ทำอย่างที่ Widor เขียนหรือใช้สองลูป ในการส่งผ่าน DataTable ครั้งแรกคุณจะเก็บเฉพาะ (ในรายการชั่วคราว) การอ้างอิงถึงแถวที่คุณต้องการลบ จากนั้นในการส่งผ่านรายการชั่วคราวของคุณครั้งที่สองคุณจะลบแถวเหล่านั้น


1
<asp:GridView ID="grd_item_list" runat="server" AutoGenerateColumns="false" Width="100%" CssClass="table table-bordered table-hover" OnRowCommand="grd_item_list_RowCommand">
    <Columns>
        <asp:TemplateField HeaderText="No">
            <ItemTemplate>
                <%# Container.DataItemIndex + 1 %>
            </ItemTemplate>
        </asp:TemplateField>            
        <asp:TemplateField HeaderText="Actions">
            <ItemTemplate>                    
                <asp:Button ID="remove_itemIndex" OnClientClick="if(confirm('Are You Sure to delete?')==true){ return true;} else{ return false;}" runat="server" class="btn btn-primary" Text="REMOVE" CommandName="REMOVE_ITEM" CommandArgument='<%# Container.DataItemIndex+1 %>' />
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

 **This is the row binding event**

protected void grd_item_list_RowCommand(object sender, GridViewCommandEventArgs e) {

    item_list_bind_structure();

    if (ViewState["item_list"] != null)
        dt = (DataTable)ViewState["item_list"];


    if (e.CommandName == "REMOVE_ITEM") {
        var RowNum = Convert.ToInt32(e.CommandArgument.ToString()) - 1;

        DataRow dr = dt.Rows[RowNum];
        dr.Delete();

    }

    grd_item_list.DataSource = dt;
    grd_item_list.DataBind();
}

1

ฉันรู้ว่านี่เป็นคำถามเก่ามากและฉันก็มีสถานการณ์คล้าย ๆ กันเมื่อไม่กี่วันก่อน

ปัญหาคือในตารางของฉันมีค่าประมาณ 10,000 แถวดังนั้นการวนซ้ำของรางDataTableจึงช้ามาก

ในที่สุดฉันก็พบวิธีแก้ปัญหาที่เร็วกว่ามากซึ่งฉันทำสำเนาแหล่งที่มาDataTableพร้อมผลลัพธ์ที่ต้องการแหล่งที่มาที่ชัดเจนDataTableและmergeผลลัพธ์จากชั่วคราวDataTableเป็นแหล่งที่มา

หมายเหตุ : แทนที่จะค้นหาJoeในDataRowชื่อnameคุณต้องค้นหาระเบียนทั้งหมดที่ไม่มีชื่อJoe(วิธีการค้นหาที่ตรงกันข้ามเล็กน้อย)

มีตัวอย่าง ( vb.net):

'Copy all rows into tmpTable whose not contain Joe in name DataRow
Dim tmpTable As DataTable = drPerson.Select("name<>'Joe'").CopyToTable
'Clear source DataTable, in Your case dtPerson
dtPerson.Clear()
'merge tmpTable into dtPerson (rows whose name not contain Joe)
dtPerson.Merge(tmpTable)
tmpTable = Nothing

ฉันหวังว่าวิธีแก้ปัญหาที่สั้นกว่านี้จะช่วยใครบางคนได้

มีc#รหัส (ไม่แน่ใจว่าถูกต้องเพราะใช้ตัวแปลงออนไลน์ :():

//Copy all rows into tmpTable whose not contain Joe in name DataRow
DataTable tmpTable = drPerson.Select("name<>'Joe'").CopyToTable;
//Clear source DataTable, in Your case dtPerson
dtPerson.Clear();
//merge tmpTable into dtPerson (rows whose name not contain Joe)
dtPerson.Merge(tmpTable);
tmpTable = null;

แน่นอนฉันใช้Try/Catchในกรณีที่ไม่มีผลลัพธ์ (ตัวอย่างเช่นหากคุณdtPersonไม่มีname Joeมันจะทำให้เกิดข้อยกเว้น) ดังนั้นคุณไม่ได้ทำอะไรกับโต๊ะของคุณมันจะไม่เปลี่ยนแปลง


0

ฉันมีชุดข้อมูลในแอพของฉันและฉันไปตั้งค่าการเปลี่ยนแปลง (ลบแถว) ไปที่มัน แต่ds.tabales["TableName"]เป็นแบบอ่านอย่างเดียว จากนั้นฉันก็พบวิธีนี้

มันเป็นC#แอพwpf

try {
    var results = from row in ds.Tables["TableName"].AsEnumerable() where row.Field<string>("Personalid") == "47" select row;                
    foreach (DataRow row in results) {
        ds.Tables["TableName"].Rows.Remove(row);                 
    }           
}

0

คุณลองทำเช่นนี้เพื่อรับและลบคอลัมน์ id จากตารางข้อมูล

if (dt1.Columns.Contains("ID"))
{
    for (int i = dt1.Rows.Count - 1; i >= 0; i--)
    {
        DataRow dr = dt1.Rows[i];

        if (dr["ID"].ToString() != "" && dr["ID"].ToString() != null)
        {
            dr.Delete();
        }
    }

    dt1.Columns.Remove("ID");
}

0

ฉันเห็นคำตอบที่ถูกต้องอยู่หลายส่วน แต่ขอฉันรวบรวมทั้งหมดและอธิบายสองสิ่งนี้

ก่อนอื่นAcceptChangesควรใช้เพื่อทำเครื่องหมายธุรกรรมทั้งหมดบนตารางว่าได้รับการตรวจสอบและยืนยันแล้วเท่านั้น ซึ่งหมายความว่าถ้าคุณกำลังใช้ DataTable เป็นแหล่งข้อมูลสำหรับการผูกกับตัวอย่างเช่นเซิร์ฟเวอร์ SQL แล้วเรียกAcceptChangesตนเองจะรับประกันได้ว่าว่าการเปลี่ยนแปลงที่ไม่เคยได้รับการบันทึกไว้ในเซิร์ฟเวอร์ SQL

สิ่งที่ทำให้ปัญหานี้สับสนมากขึ้นคือมีสองกรณีที่เกิดข้อยกเว้นและเราต้องป้องกันทั้งสองกรณี

1. การแก้ไขคอลเล็กชันของ IEnumerable

เราไม่สามารถเพิ่มหรือลบดัชนีในคอลเลกชันที่กำลังแจกแจงได้เนื่องจากการทำเช่นนั้นอาจส่งผลต่อการจัดทำดัชนีภายในของตัวแจงนับ มีสองวิธีในการแก้ไขปัญหานี้: ทำดัชนีของคุณเองใน for loop หรือใช้คอลเลกชันแยกต่างหาก (ที่ไม่ได้แก้ไข) สำหรับการแจงนับ

2. พยายามอ่านรายการที่ถูกลบ

เนื่องจาก DataTables เป็นคอลเลกชันธุรกรรมรายการสามารถถูกทำเครื่องหมายเพื่อลบ แต่ยังคงปรากฏในการแจงนับ ซึ่งหมายความว่าหากคุณถามรายการที่ถูกลบสำหรับคอลัมน์"name"นั้นจะทำให้เกิดข้อยกเว้น ซึ่งหมายความว่าเราต้องตรวจสอบว่าdr.RowState != DataRowState.Deletedก่อนที่จะค้นหาคอลัมน์

วางมันทั้งหมดเข้าด้วยกัน

เราอาจจะยุ่งและทำทุกอย่างด้วยตนเองหรือเราสามารถปล่อยให้ DataTable ทำงานทั้งหมดให้เราและทำให้คำสั่งมีลักษณะเหมือนการเรียก SQL โดยทำดังต่อไปนี้

string name = "Joe";
foreach(DataRow dr in dtPerson.Select($"name='{name}'"))
    dr.Delete();

ด้วยการเรียกSelectใช้ฟังก์ชันของ DataTable แบบสอบถามของเราจะหลีกเลี่ยงรายการที่ลบไปแล้วใน DataTable โดยอัตโนมัติ และเนื่องจากSelectฟังก์ชันส่งคืนอาร์เรย์ของการจับคู่คอลเลคชันที่เรากำลังแจกแจงอยู่จะไม่ถูกแก้ไขเมื่อเราเรียกdr.Delete()ใช้ ฉันยังเพิ่มนิพจน์ Select ด้วยการแก้ไขสตริงเพื่อให้สามารถเลือกตัวแปรได้โดยไม่ทำให้โค้ดมีเสียงดัง


0

วิธีง่ายๆใช้ปุ่มนี้ใน:

 var table = $('#example1').DataTable();
 table.row($(`#yesmediasec-${id}`).closest('tr')).remove( ).draw();

example1 = ตาราง id yesmediasec = id ของปุ่มในแถว

ใช้แล้วทุกอย่างจะโอเค

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