从DataTable中访问已删除的行

7
我有一个父WinForm,其中有一个名为MyDataTable的成员变量_dt。MyDataTable类型是在Visual Studio 2005中的“类型化数据集”设计工具中创建的(MyDataTable继承自DataTable),通过ADO.NET从数据库中填充_dt。根据用户在表单中的交互更改,我会像这样从表中删除一行: _dt.FindBySomeKey(_someKey).Delete();
稍后,_dt按值传递到对话框窗体。从那里开始,我需要扫描所有行以构建一个字符串:
           foreach (myDataTableRow row in _dt)
            {
                sbFilter.Append("'" + row.info + "',");
            }

问题是,在删除后进行此操作时,会抛出以下异常:DeletedRowInaccessibleException: Deleted row information cannot be accessed through the row. 我目前使用的解决方法(感觉像是一种hack)如下:
           foreach (myDataTableRow  row in _dt)
            {
                if (row.RowState != DataRowState.Deleted &&
                    row.RowState != DataRowState.Detached)
                {
                    sbFilter.Append("'" + row.info + "',");
                }
            }

我的问题是:这是正确的做法吗?为什么foreach循环会访问通过Delete()方法标记的行?


你不需要检查 Detached - SLaks
5个回答

14

Delete 方法 会标记一行进行删除;只有在调用 AcceptChanges 方法之后,该行才会真正被移除。

相反,调用 _dt.Rows.Remove(_dt.FindBySomeKey(_someKey)),这也会接受更改。
难以置信的是,Rows.Remove 会完全移除该行,而 row.Delete() 则不会
需要注意的是,如果调用了 Rows.Remove,那么该行将永久消失,并且 DataAdapter 不会从数据库中删除它。

在 C# 3 中,您可以使用以下扩展方法替换您的 if 语句:

///<summary>Gets the rows in a typed DataTable that have not been deleted.</summary>
public static EnumerableRowCollection<TRow> CurrentRows<TRow>(this TypedTableBase<TRow> table) where TRow : DataRow { 
    return table.Where(r => r.RowState != DataRowState.Deleted); 
}

foreach (myDataTableRow row in _dt.CurrentRows())
{
    ...

编辑

这里是一个C# 2版本:

static class Utils {
    public static IEnumerable<TRow> CurrentRows(IEnumerable<TRow> table) where TRow : DataRow {
        foreach(TRow row in table) {
            if (row.RowState != DataRowState.Deleted)
                yield return row;
        }
    }
}


foreach (myDataTableRow row in Utils.CurrentRows(_dt))
{
您也可以将此函数放在类型表的部分类中:
partial class MyTable {
    public IEnumerable<MyRow> CurrentRows() { return Utils.CurrentRows(this); }
}

感谢您的回复。通过这种方式操作,当我尝试将表更新回数据库时,会有什么影响吗?_dt.Update()Update() 方法是否知道使用 DeleteCommand 删除此行,还是它已经“永远消失”了? - Ken
@Ken:不,被删除的行在更新期间不会被删除。Remove(移除)!= Delete(删除) - H H
@Ken:好观点;我会认为调用remove将导致Update不会删除它。试试看。 - SLaks
@SLaks - 调用Remove会导致Update在数据库中不删除它。此外,C#3对我无效(使用2.0),但感谢您的建议! - Ken
1
@Slaks,你的C# 2版本不正确 => public static IEnumerable<TRow> CurrentRows<TRow>(this IEnumerable<TRow> table) where TRow : DataRow { foreach(TRow row in table) { if (row.RowState != DataRowState.Deleted) yield return row; } } - Elisabeth

8
你在迭代中跳过行的做法是正确的,我经常在循环遍历可能被修改的DataTable时检查RowState。
在某些情况下,我认为你需要在标记为删除之前获取原始行值。在检索特定数据行值时,有一个辅助索引选项。
string st = dr["columnname", DataRowVersion.Original].ToString(); // c#

dim st As String = dr("columnname", DataRowVersion.Original).ToString() ' vb.net

我曾多次遇到这个问题。

关于GetChanges(RowState)方法,不要忘记检查返回值是否为空,如果没有符合条件的行,返回的DataTable将为null(我认为它应该返回一个零行的表)。


2

您需要检查以下内容:

 _dt.DefaultView.RowStateFilter

我认为默认设置不会显示已删除的行,也许您在某个地方更改了它。

您可以始终创建额外的RowView并控制其过滤器,然后在View上执行循环。


那不会影响表的枚举器。 - SLaks
@Slaks: 我写了:“循环查看” - H H
@Henk:最终我使用了DataView进行循环(默认情况下不包含已删除的记录),代码如下:foreach (DataRowView row in _dt.DefaultView ) { sbFilter.Append("'" + ((myDataTableRow)row.Row).info+ "',"); }在所有提供的选项中,我认为这是最简洁的方法。谢谢! - Ken

1

使用 GetChanges():

foreach (myDataTableRow row in _dt.GetChanges(DataRowState.Added))
{
    sbFilter.Append("'" + row.info + "',");
}

如果您想获取所有已添加和修改的内容,请对Added和Modified使用按位OR运算符:

foreach (myDataTableRow row in
    _dt.GetChanges(DataRowState.Added | DataRowState.Modified))
{
    sbFilter.Append("'" + row.info + "',");
}

1
这个想法是正确的,但实际上会产生编译器错误,因为DataTable不包含公共的GetEnumerator()方法。 - Ken
这不会返回未更改的行。 - SLaks
@SLaks:尽管名为GetChanges,但您可以获取未更改的行,_dt.GetChanges(DataRowSate.Unchanged)。 - Michael Buen

0
我不确定,但我认为如果您在删除一个或多个行后调用 _dt.AcceptChanges(),在您遍历数据表的 Rows 集合时,您将无法“看到”已删除的行。

AcceptChanges方法会移除行的状态,但我需要在执行_dt.Update()将更改持久化到数据库时保留该状态。我可能会在更新后调用AcceptChanges()方法。谢谢! - Ken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接