DataTable'dan belirli satırları silme


87

DataTable'dan bazı satırları silmek istiyorum ama böyle bir hata veriyor,

Koleksiyon değiştirildi; numaralandırma işlemi yürütülemeyebilir

Bu kodu silmek için kullanıyorum,

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

Peki sorun nedir ve nasıl düzeltilir? Hangi yöntemi tavsiye edersiniz?

Yanıtlar:


170

Bir koleksiyondan bir öğeyi silerseniz, bu koleksiyon değiştirilmiştir ve onun üzerinden numaralandırmaya devam edemezsiniz.

Bunun yerine, aşağıdaki gibi bir For döngüsü kullanın:

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

Geçerli dizini sildikten sonra bir satırı atlamaktan kaçınmak için tersine yinelediğinizi unutmayın.


@Slugster beni yendi! (Ben senin değişti [ii]To [i]:-) Ancak
Widor

11
Bu yanlış. Sen edebilirsiniz satırları silerken bir tablo döngü için bir foreach kullanın. Steve'in cevabına bakın .
Alexander Garden

3
Bu cevap aynı zamanda @ bokkie'nin cevabını da içermelidir. Sonrasını kullanırsak, DataTablebir istisna oluşturacaktır. Doğru yol Remove(), kaynağı aramak olacaktır DataTable- dtPerson.Rows.Remove(dr).
Code.me

Bir veritabanı sunucusundaki bir tabloyu güncellemek için DataTable kullanıyorsanız, @Steve'nin daha iyi bir cevabı var. Tek bir döngüde satırları silinmiş olarak işaretleyebilir, satırları güncelleyebilir ve yeni satırlar ekleyebilirsiniz. Değişiklikleri Db tablosuna uygulamak için bir SqlAdapter kullanabilirsiniz. Sorunun ne sıklıkla ortaya çıktığı düşünüldüğünde, tüm süreç olması gerektiğini düşündüğünüzden çok daha karmaşık, ancak işe yarıyor. DataTable'ın işlemsel doğasından yararlanmayacak olsaydım, sadece bir nesne koleksiyonu ve namco veya Widor yaklaşımını kullanırdım.
BH

Kullanmak , silme işleminin etkili olması için Delete()bir çağrı gerektirmez AcceptChanges()mi?
Broots Waymb

128

Herkes ' Bir Numaralandırma'daki satırları silemezsiniz ' kervanına atlamadan önce, önce DataTable'ların işlemsel olduğunu anlamanız ve AcceptChanges () çağırana kadar değişiklikleri teknik olarak temizlememeniz gerekir.

Sil'i ararken bu istisnayı görüyorsanız , zaten değişiklik bekleyen bir veri durumundasınız demektir . Örneğin, veritabanından yeni yüklediyseniz, bir foreach döngüsünün içindeyseniz Delete çağrısı bir istisna atacaktır.

FAKAT! FAKAT!

Veritabanından satırlar yüklerseniz ve ' AcceptChanges () ' işlevini çağırırsanız, bekleyen tüm değişiklikleri DataTable'a dahil edersiniz. Artık dünyada umursamadan Delete () 'i çağıran satırların listesini yineleyebilirsiniz, çünkü yalnızca Silme satırını kulak işaretiyle işaretler, ancak siz tekrar arayana kadar taahhüt edilmez AcceptChanges) (

Bu yanıtın biraz eski olduğunun farkındayım, ancak yakın zamanda benzer bir sorunla uğraşmak zorunda kaldım ve umarım bu, 10 yıllık kod üzerinde çalışan gelecekteki bir geliştirici için biraz acı çeker :)


Ps İşte Jeff tarafından eklenen basit bir kod örneği :

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

c # sürümü için () yerine {ve} kullanmanız gerekir
BugLover

2
Ayrıca (Sanırım) daha yararlı değiştirmek object row_loopVariable in ds.Tables(0).RowsiçinDataRow row in ds.Tables(0).Rows
BugLover

2
Ffs, bu beni bir hafta sonu kabusu konuşlandırması sırasında kurtardı. Tüm biraları hak ediyorsun!
James Love


Güzel kod. Bir şey, C # 'da bir artırmanın tipik yolu counter++yerine counter+= 1.
MQuiggGeorgia

18

bu çözümle:

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

Satırı sildikten sonra datatable'ı kullanacaksanız, bir hata alırsınız. Ne yapabilirim yani: yerine dr.Delete();iledtPerson.Rows.Remove(dr);


16

Bu benim için çalışıyor

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

"Ayrıca, yalnızca bir satırı kaldırılmak üzere işaretlemek için DataRow sınıfının Delete yöntemini de çağırabilirsiniz. Remove'u çağırmak, Delete'i ve ardından AcceptChanges'ı çağırmakla aynıdır. Remove, bir DataRowCollection nesnesi aracılığıyla yinelenirken foreach döngüsünde çağrılmamalıdır. koleksiyonun durumunu değiştirir. " Bkz msdn.microsoft.com/de-de/library/... Şerefe.
Andreas Krohn

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

Umarım bu sana yardımcı olur


1
komut drow.Delete();değil drow.delete();yöntemler büyük / küçük harfe duyarlıdır .net btw
MethodMan

5

Kaldırmak için tüm satırı gelen DataTable , böyle yapmak

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

Veya bir DataTable Satır koleksiyonunu bir listeye dönüştürün :

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

1

Sorun nerede: Bir foreach döngüsü içindeki koleksiyondan öğeleri silmek yasaktır.

Çözüm: Ya Widor'un yazdığı gibi yapın ya da iki döngü kullanın. DataTable üzerinden ilk geçişte, yalnızca silmek istediğiniz satırların referanslarını (geçici bir listede) saklarsınız. Ardından, geçici listenizin üzerinden ikinci geçişte bu satırları silersiniz.


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

Bunun çok eski bir soru olduğunu biliyorum ve birkaç gün önce benzer bir durumla karşılaştım.

Sorun, masamda yaklaşık. 10000 satır, bu yüzden döngü çukurDataTable satırlar çok yavaştı.

Sonunda, kaynağın kopyasını DataTableistediğim sonuçlarla, net kaynakla DataTableve mergegeçici sonuçtan DataTablekaynak bire sonuçlarla kopyaladığım çok daha hızlı bir çözüm buldum .

Not : yerine aramak Joeiçinde DataRowdenilen nameSen, adı yok tüm kayıtlar aramak zorunda Joe(arama küçük ters bir şekilde)

Örnek var ( 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

Umarım bu kısa çözüm birilerine yardımcı olur.

Orada c#kodu (emin değilim ben :( çevrimiçi dönüştürücü kullanılan çünkü düzeltmek ise):

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

Elbette, Try/Catchsonuç olmaması durumunda kullandım (örneğin, eğer siz dtPersoniçermezseniz name Joeistisna atar), bu yüzden masanızla hiçbir şey yapmazsınız, değişmeden kalır.


0

Uygulamamda bir veri kümem var ve değişiklik yapmaya (bir satırı silerek) gittim ancak ds.tabales["TableName"]salt okunur. Sonra bu çözümü buldum.

Bu bir wpf C#uygulaması,

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

Bunu, kimlik sütununu veri tablosundan almak ve kaldırmak için deneyin

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

Burada doğru cevabın çeşitli parçalarını ve parçalarını görüyorum, ancak hepsini bir araya getirip birkaç şeyi açıklamama izin verin.

Her şeyden önce, AcceptChangesyalnızca bir tablodaki tüm işlemin doğrulanmış ve gerçekleştirilmiş olarak işaretlenmesi için kullanılmalıdır. Bu, DataTable'ı örneğin bir SQL sunucusuna bağlanmak için bir DataSource olarak kullanıyorsanız, AcceptChangesmanuel olarak çağırmanın değişikliklerin hiçbir zaman SQL sunucusuna kaydedilmeyeceğini garanti edeceği anlamına gelir .

Bu konuyu daha kafa karıştırıcı kılan şey, aslında istisnanın fırlatıldığı iki durum olması ve ikisini de önlememiz gerektiğidir.

1. Bir IEnumerable Koleksiyonunu Değiştirme

Numaralandırılan koleksiyona bir dizin ekleyemeyiz veya kaldıramayız çünkü bunu yapmak, numaralandırıcının dahili indekslemesini etkileyebilir. Bunu aşmanın iki yolu vardır: ya bir for döngüsünde kendi indekslemenizi yapın ya da numaralandırma için ayrı bir koleksiyon (değiştirilmemiş) kullanın.

2. Silinmiş bir Girdiyi Okumaya Çalışmak

DataTables işlem koleksiyonları olduğundan, girişler silinmek üzere işaretlenebilir ancak yine de numaralandırmada görünebilir. Bu, sütun için silinmiş bir giriş sorarsanız, "name"bir istisna atacağı anlamına gelir . Bu, dr.RowState != DataRowState.Deletedbir sütunu sorgulamadan önce kontrol etmemiz gerektiği anlamına gelir .

Hepsini bir araya koy

Dağınık olabilir ve hepsini manuel olarak yapabiliriz veya DataTable'ın bizim için tüm işi yapmasına ve aşağıdakileri yaparak ifadenin daha çok bir SQL çağrısı gibi görünmesine ve daha çok SQL çağrısına benzemesine izin verebiliriz:

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

DataTable'ın Selectişlevini çağırarak, sorgumuz DataTable'da önceden silinmiş girişleri otomatik olarak önler. Ve Selectişlev bir eşleşme dizisi döndürdüğünden, üzerinde numaralandırdığımız koleksiyon çağırdığımızda değiştirilmez dr.Delete(). Ayrıca, kodu gürültülü hale getirmeden değişken seçimine izin vermek için dize enterpolasyonu ile Select ifadesini de renklendirdim.


0

bunu düğmede kullanmanın kolay yolu:

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

example1 = id tablosu. yesmediasec = satırdaki düğmenin kimliği

kullan ve her şey yoluna girecek

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.