Xóa các hàng cụ thể khỏi DataTable


85

Tôi muốn xóa một số hàng khỏi DataTable, nhưng nó báo lỗi như thế này,

Bộ sưu tập đã được sửa đổi; thao tác liệt kê có thể không thực hiện

Tôi sử dụng để xóa mã này,

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

Vì vậy, vấn đề là gì và làm thế nào để khắc phục nó? Bạn tư vấn phương pháp nào?

Câu trả lời:


169

Nếu bạn xóa một mục khỏi bộ sưu tập, bộ sưu tập đó đã bị thay đổi và bạn không thể tiếp tục liệt kê qua bộ sưu tập đó.

Thay vào đó, hãy sử dụng vòng lặp For, chẳng hạn như:

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

Lưu ý rằng bạn đang lặp lại ngược lại để tránh bỏ qua một hàng sau khi xóa chỉ mục hiện tại.


@Slugster đã đánh bại tôi với nó! (Tôi đã đổi của bạn [ii]thành [i], tuy nhiên :-)
Widor

11
Điều này là không chính xác. Bạn có thể sử dụng foreach để lặp qua bảng trong khi xóa các hàng. Xem câu trả lời của Steve .
Alexander Garden,

3
Câu trả lời này cũng nên bao gồm câu trả lời của @ bokkie. Nếu chúng tôi sử dụng DataTablesau này, nó sẽ ném ra một ngoại lệ. Cách chính xác sẽ là gọi Remove()nguồn DataTable- dtPerson.Rows.Remove(dr).
Code.me

Nếu bạn đang sử dụng DataTable để cập nhật bảng trong máy chủ cơ sở dữ liệu, @Steve có câu trả lời tốt hơn. Bạn có thể đánh dấu các hàng là đã xóa, cập nhật các hàng và thêm các hàng mới trong một vòng lặp. Bạn có thể sử dụng SqlAdapter để cam kết các thay đổi đối với bảng Db. Xem xét mức độ thường xuyên của vấn đề phát sinh, toàn bộ quá trình phức tạp hơn bạn nghĩ rất nhiều, nhưng nó hoạt động. Nếu tôi không tận dụng tính chất giao dịch của DataTable, tôi sẽ chỉ sử dụng một tập hợp đối tượng và phương pháp namco hoặc Widor.
BH

Việc sử dụng không Delete()yêu cầu một cuộc gọi đến AcceptChanges()để xóa có hiệu lực?
Broots Waymb

128

Trước khi mọi người nhảy vào dải băng ' Bạn không thể xóa hàng trong Bảng liệt kê ', trước tiên bạn cần nhận ra rằng Bảng dữ liệu là giao dịch và không xóa các thay đổi về mặt kỹ thuật cho đến khi bạn gọi AcceptChanges ()

Nếu bạn thấy ngoại lệ này trong khi gọi Xóa , bạn đã ở trạng thái dữ liệu thay đổi đang chờ xử lý . Ví dụ: nếu bạn vừa tải từ cơ sở dữ liệu, việc gọi Delete sẽ đưa ra một ngoại lệ nếu bạn đang ở trong một vòng lặp foreach.

NHƯNG! NHƯNG!

Nếu bạn tải các hàng từ cơ sở dữ liệu và gọi hàm ' AcceptChanges () ', bạn cam kết tất cả các thay đổi đang chờ xử lý đó cho DataTable. Giờ đây, bạn có thể lặp lại danh sách các hàng đang gọi Delete () mà không cần quan tâm, bởi vì nó chỉ đơn giản là đánh dấu hàng cho Xóa, nhưng không được cam kết cho đến khi bạn gọi lại AcceptChanges ()

Tôi nhận ra rằng phản hồi này hơi lỗi thời, nhưng tôi đã phải đối phó với một vấn đề tương tự gần đây và hy vọng điều này sẽ giảm bớt một phần đau đớn cho một nhà phát triển tương lai làm việc trên mã 10 tuổi :)


Ps Đây là một ví dụ mã đơn giản được thêm bởi Jeff :

C #

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

cho phiên bản c # chỉ cần sử dụng {và} thay vì ()
BugLover

2
cũng hữu ích hơn (tôi nghĩ) để thay đổi object row_loopVariable in ds.Tables(0).RowsthànhDataRow row in ds.Tables(0).Rows
BugLover

2
Ffs, điều này đã cứu tôi trong một lần triển khai cuối tuần ác mộng. Bạn xứng đáng với tất cả các loại bia!
James Love,


Mã đẹp. Một điều, trong C #, cách điển hình để tăng từng một là counter++thay vì counter+= 1.
MQuiggGeorgia

18

với giải pháp này:

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

nếu bạn định sử dụng dữ liệu sau khi xóa hàng, bạn sẽ gặp lỗi. Vì vậy, những gì bạn có thể làm là: thay thế dr.Delete();bằngdtPerson.Rows.Remove(dr);


16

Điều này phù hợp với tôi,

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

quá dễ dàng để bỏ lỡ dt.AcceptChanges ()
Matthew Lock

"Bạn cũng có thể gọi phương thức Delete của lớp DataRow để chỉ cần đánh dấu một hàng để xóa. Gọi Remove cũng giống như gọi Delete và sau đó gọi AcceptChanges. Remove không nên được gọi trong vòng lặp foreach khi lặp qua đối tượng DataRowCollection. sửa đổi trạng thái của bộ sưu tập. " Xem msdn.microsoft.com/de-de/library/… Chúc mừng.
Andreas Krohn

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

Tôi hy vọng nó sẽ giúp bạn


1
lệnh là drow.Delete();không drow.delete();phương pháp này là trường hợp nhạy cảm trong .net btw
MethodMan

5

Để xóa toàn bộ hàng khỏi DataTable , hãy làm như thế này

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

Hoặc chỉ cần chuyển đổi bộ sưu tập Hàng DataTable thành một danh sách:

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

1

Vấn đề là ở đâu: Không được phép xóa các mục khỏi bộ sưu tập bên trong vòng lặp foreach.

Giải pháp: Làm điều đó như Widor đã viết hoặc sử dụng hai vòng lặp. Trong lần chuyển đầu tiên qua DataTable, bạn chỉ lưu trữ (trong danh sách tạm thời) các tham chiếu đến các hàng bạn muốn xóa. Sau đó, trong lần thứ hai chuyển qua danh sách tạm thời của bạn, bạn xóa các hàng đó.


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

Tôi biết đây là câu hỏi rất cũ và tôi cũng gặp phải trường hợp tương tự cách đây vài ngày.

Vấn đề là, trong bảng của tôi là khoảng. 10000 hàng, vì vậy DataTablecác hàng đáy lặp lại rất chậm.

Cuối cùng, tôi đã tìm thấy giải pháp nhanh hơn nhiều, nơi tôi tạo bản sao của nguồn DataTablevới kết quả mong muốn, nguồn rõ ràng DataTablemergekết quả từ tạm thời DataTablethành nguồn một.

lưu ý : thay vì tìm kiếm Joetrong DataRowđược gọi nameBạn phải tìm kiếm tất cả các bản ghi không có tên Joe(cách tìm kiếm hơi ngược lại)

Có ví dụ ( 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

Tôi hy vọng vì vậy giải pháp ngắn hơn này sẽ giúp ai đó.

c#mã (không chắc có đúng không vì tôi đã sử dụng công cụ chuyển đổi trực tuyến :():

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

Tất nhiên, tôi đã sử dụng Try/Catchtrong trường hợp nếu không có kết quả (ví dụ: nếu dtPersonKhông chứa của name Joebạn sẽ ném ra ngoại lệ), vì vậy Bạn không làm gì với bảng của Bạn, nó vẫn không thay đổi.


0

Tôi có một tập dữ liệu trong ứng dụng của mình và tôi đã đặt các thay đổi (xóa một hàng) cho nó nhưng ds.tabales["TableName"] được đọc. Sau đó, tôi tìm thấy giải pháp này.

Đó là một C#ứng dụng 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

Bạn thử điều này để lấy và xóa cột id khỏi bảng dữ liệu

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

Tôi đang thấy nhiều mẩu tin khác nhau của câu trả lời đúng ở đây, nhưng hãy để tôi tập hợp tất cả lại với nhau và giải thích một vài điều.

Trước hết, AcceptChangeschỉ nên được sử dụng để đánh dấu toàn bộ giao dịch trên bảng là đã được xác thực và cam kết. Điều đó có nghĩa là nếu bạn đang sử dụng DataTable làm Nguồn dữ liệu để liên kết, ví dụ: máy chủ SQL, thì việc gọi AcceptChangestheo cách thủ công sẽ đảm bảo rằng các thay đổi không bao giờ được lưu vào máy chủ SQL .

Điều làm cho vấn đề này trở nên khó hiểu hơn là thực sự có hai trường hợp trong đó ngoại lệ được ném ra và chúng ta phải ngăn chặn cả hai trường hợp đó.

1. Sửa đổi Bộ sưu tập của IEnumerable

Chúng tôi không thể thêm hoặc xóa một chỉ mục vào tập hợp đang được liệt kê vì làm như vậy có thể ảnh hưởng đến việc lập chỉ mục nội bộ của điều tra viên. Có hai cách để giải quyết vấn đề này: hoặc lập chỉ mục của riêng bạn trong vòng lặp for, hoặc sử dụng một tập hợp riêng biệt (không được sửa đổi) cho việc liệt kê.

2. Cố gắng đọc một mục đã xóa

Vì Bảng dữ liệu là bộ sưu tập giao dịch , các mục nhập có thể được đánh dấu để xóa nhưng vẫn xuất hiện trong bảng liệt kê. Có nghĩa là nếu bạn yêu cầu một mục đã xóa cho cột "name"thì nó sẽ ném ra một ngoại lệ. Có nghĩa là chúng ta phải kiểm tra xem liệu dr.RowState != DataRowState.Deletedtrước khi truy vấn một cột.

Để tất cả chúng cùng nhau

Chúng tôi có thể lộn xộn và thực hiện tất cả những việc đó theo cách thủ công hoặc chúng tôi có thể để DataTable thực hiện tất cả công việc cho chúng tôi và làm cho câu lệnh trông giống như một lệnh gọi SQL bằng cách thực hiện như sau:

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

Bằng cách gọi Selecthàm của DataTable , truy vấn của chúng tôi sẽ tự động tránh các mục nhập đã bị xóa trong DataTable. Và vì Selecthàm trả về một mảng so khớp, nên tập hợp chúng ta đang liệt kê sẽ không bị sửa đổi khi chúng ta gọi dr.Delete(). Tôi cũng đã thêm gia vị cho biểu thức Chọn với nội suy chuỗi để cho phép lựa chọn biến mà không làm cho mã bị nhiễu.


0

cách dễ dàng sử dụng nút này trong:

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

example1 = id bảng. yesmediasec = id của nút trong hàng

sử dụng nó và mọi thứ sẽ ổn

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.