DataTable에서 일부 행을 삭제하고 싶지만 다음과 같은 오류가 발생합니다.
컬렉션이 수정되었습니다. 열거 작업이 실행되지 않을 수 있음
이 코드를 삭제하는 데 사용합니다.
foreach(DataRow dr in dtPerson.Rows){
if(dr["name"].ToString()=="Joe")
dr.Delete();
}
그렇다면 문제는 무엇이며 어떻게 해결합니까? 어떤 방법을 권합니까?
답변:
컬렉션에서 항목을 삭제하면 해당 컬렉션이 변경된 것이므로 계속 열거 할 수 없습니다.
대신 다음과 같은 For 루프를 사용하십시오.
for(int i = dtPerson.Rows.Count-1; i >= 0; i--)
{
DataRow dr = dtPerson.Rows[i];
if (dr["name"] == "Joe")
dr.Delete();
}
dtPerson.AcceptChanges();
현재 인덱스를 삭제 한 후 행을 건너 뛰지 않도록 역순으로 반복합니다.
DataTable
나중에 사용하면 예외가 발생합니다. 올바른 방법은 호출하는 것입니다 Remove()
소스에 DataTable
- dtPerson.Rows.Remove(dr)
.
Delete()
에 대한 호출을 필요로 AcceptChanges()
적용하려면 삭제하려면?
모든 사람이 ' 열거 형에서 행을 삭제할 수 없습니다 '라는 유행에 뛰어 들기 전에 먼저 DataTables가 트랜잭션 적이라는 것을 인식하고 AcceptChanges () 를 호출 할 때까지 기술적으로 변경 사항을 제거하지 않아야합니다.
Delete 를 호출하는 동안이 예외가 표시되는 경우 이미 변경 보류중인 데이터 상태 입니다. 예를 들어, 데이터베이스에서 방금로드 한 경우, foreach 루프 내에있는 경우 Delete를 호출하면 예외가 발생합니다.
그러나! 그러나!
데이터베이스에서 행을로드하고 ' AcceptChanges () ' 함수를 호출하면 보류중인 모든 변경 사항을 DataTable에 커밋합니다. 이제 Delete ()를 호출하는 행 목록을 순회 할 수 있습니다. 단순히 행을 Deletion 용으로 표시하지만 AcceptChanges () 를 다시 호출 할 때까지 커밋되지 않기 때문입니다.
나는이 응답이 약간 오래된 것임을 알고 있지만 최근에 비슷한 문제를 다루어야했고 이것이 10 년 된 코드를 작업하는 미래의 개발자에게 약간의 고통을 덜어주기를 바랍니다. :)
추신 다음은 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()
object row_loopVariable in ds.Tables(0).Rows
에DataRow row in ds.Tables(0).Rows
counter++
은 counter+= 1
.
이것은 나를 위해 작동합니다.
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();
<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();
}
나는 이것이 매우 오래된 질문이라는 것을 알고 있으며 며칠 전에 비슷한 상황이 발생했습니다.
문제는 내 테이블에 약입니다. 10000 개의 행이므로 루핑 트로프 DataTable
행은 매우 느 렸습니다.
마지막으로 DataTable
원하는 결과, 명확한 소스 DataTable
및 merge
임시 결과 DataTable
를 소스 1로 복사하는 훨씬 빠른 솔루션을 찾았습니다 .
참고 : 대신 호출 됨을 검색합니다 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
결과가없는 경우에 사용 했으므로 (예 : Your dtPerson
do n't contain name
Joe
it will throw exception will throw), You do nothing with your table, 그것은 변경되지 않은 상태로 유지됩니다.
내 앱에 데이터 세트가 있고 변경 사항 (행 삭제)을 설정하려고했지만 ds.tabales["TableName"]
읽기 전용입니다. 그런 다음이 해결책을 찾았습니다.
WPF C#
앱입니다.
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);
}
}
여기에서 정답의 여러 부분을보고 있지만 모두 모아서 몇 가지를 설명하겠습니다.
우선 AcceptChanges
테이블의 전체 트랜잭션을 유효성 검사 및 커밋 된 것으로 표시하는 데만 사용해야합니다. 즉, 예를 들어 SQL 서버에 바인딩하기 위해 DataTable을 데이터 소스로 사용하는 경우 AcceptChanges
수동으로 호출 하면 변경 사항 이 SQL 서버에 저장되지 않습니다 .
이 문제를 더 혼란스럽게 만드는 것은 실제로 예외가 발생하는 두 가지 경우가 있으며 둘 다 방지해야한다는 것입니다.
1. IEnumerable의 컬렉션 수정
열거 자의 내부 인덱싱에 영향을 미칠 수 있으므로 열거중인 컬렉션에 인덱스를 추가하거나 제거 할 수 없습니다. 이 문제를 해결하는 방법에는 두 가지가 있습니다. for 루프에서 자체 인덱싱을 수행하거나 열거 형에 대해 별도의 컬렉션 (수정되지 않은)을 사용하는 것입니다.
2. 삭제 된 항목 읽기 시도
DataTable은 트랜잭션 컬렉션이므로 항목을 삭제 표시 할 수 있지만 여전히 열거 형에 나타납니다. 즉, 열에 대해 삭제 된 항목을 요청 "name"
하면 예외가 발생합니다. 즉 dr.RowState != DataRowState.Deleted
, 열을 쿼리하기 전에 확인해야합니다 .
함께 모아서
우리는 지저분 해지고 모든 작업을 수동으로 수행하거나 DataTable이 모든 작업을 수행하도록하고 다음을 수행하여 명령문을 SQL 호출처럼 보이게 만들 수 있습니다.
string name = "Joe";
foreach(DataRow dr in dtPerson.Select($"name='{name}'"))
dr.Delete();
DataTable의 Select
함수 를 호출 하면 쿼리가 DataTable에서 이미 삭제 된 항목을 자동으로 방지합니다. 그리고이 Select
함수는 일치하는 배열을 반환하기 때문에 열거하고있는 컬렉션은를 호출 할 때 수정되지 않습니다 dr.Delete()
. 또한 코드를 시끄럽게 만들지 않고 변수를 선택할 수 있도록 문자열 보간을 사용하여 Select 표현식을 꾸몄습니다.
[ii]
에를[i]
:-) 그러나,