使用数据集的更有效方式

Posted

技术标签:

【中文标题】使用数据集的更有效方式【英文标题】:A more efficient way to work with DataSets 【发布时间】:2010-11-19 09:08:33 【问题描述】:

我有以下代码,在每个Form 上重复,作为更新过程的一部分。当页面加载时,BLL 返回一个DataSet,比如

_personInfo = ConnectBLL.BLL.Person.GetPerson(personID);

我将DataSet 存储在Form 级别变量中,然后我用它来检查验证/更新过程中的更改。我一次将一行(尽管永远不会超过一行)传递给Function,它获取控件中的值并将其与DataSet 中的相应列值进行比较。如果发现不同,则将该列 = 设置为新值,并将名称添加到更改内容的 List

// Load Person info
        using (var tmpPersonDT = tmpPersonDS.Tables[0])
        
            if (tmpPersonDT.Rows.Count > 0)
            
                foreach (DataRow row in tmpPersonDT.Rows)
                
                    CheckPersonData(row);
                

            
        

// Snippet of the CheckPersonData() that is being called....
    if (!object.Equals(row["ResidencyCountyID"], lkuResidenceCounty.EditValue))
    
        row["ResidencyCountyID"] = lkuResidenceCounty.EditValue;
        _whatChanged.Add("ResidencyCounty");
    

if (!object.Equals(row["ResponsibilityCountyID"], lkuResponsibleCounty.EditValue))

    row["ResponsibilityCountyID"] = lkuResponsibleCounty.EditValue;
    _whatChanged.Add("ResponsibilityCounty");


if (!object.Equals(row["HispanicOriginFlag"], chkHispanic.EditValue))

    row["HispanicOriginFlag"] = chkHispanic.EditValue;
    _whatChanged.Add("HispanicOriginFlag");


if (!object.Equals(row["CitizenFlag"], chkCitizen.EditValue))

    row["CitizenFlag"] = chkCitizen.EditValue;
    _whatChanged.Add("CitizenFlag");


if (!object.Equals(row["VeteranFlag"], chkVeteran.EditValue))

    row["VeteranFlag"] = chkVeteran.EditValue;
    _whatChanged.Add("VeteranFlag");

我想要回答的是,这真的是解决这个问题的最有效方法吗?

如果没有别的,我想创建一个函数来进行比较,而不是重复 30 次(每种形式不同),但我无法完全弄清楚。我想也许我可以使用 row[].ItemArray 但那只有值。我必须提前知道这些物品的顺序并保证它们不会改变......

CRUD 应用程序中使用 DataSets/DataTables 时我是否遗漏了一些明显的东西?


juliandewitt's post below is fantastic!

我只是,现在,需要一些关于如何在上面使用它的指导。任何人都可以指出我的任何链接将不胜感激。如果你能发布一个例子就更好了。

这样使用 DataRows 有什么缺点吗?

【问题讨论】:

whew 我认为这是我有史以来最大的答案 :) 说真的,我希望在内置状态跟踪、强类型 DataSet 和 TableAdapter 以及我们拥有的 DataBinding 之间为您打开了一些大门,让您可以在 Visual Studio 中找到一些 RAD 工具以及 ADO.NET 的强大功能。也就是说,它不是灵丹妙药,也不是完美的系统。这是一个具有缺陷和局限性的广泛系统,但它是 .NET 1.0 - 2.0 的基石。然而,.NET 3.0 及更高版本开始远离 ADO.NET,并且在 3.5 SP1 中实体框架开始显现。不过,这一切仍然值得了解! 【参考方案1】:

看起来您正在做大量的体力劳动,这可以通过将您的控件直接数据绑定到您的数据集/表来减轻。数据绑定将您的数据源(在本例中为您的数据集/表)与您的 UI 连接在一起。当 UI 中的值发生变化时,它会更新数据源。

DataBinding 是一个值得研究和测试的BIG 主题。将数据绑定到 DataTable/Set 时存在一些问题(在当前行更改之前不会提交行更改,这在您一次只处理一行的情况下很烦人——但有一些变通方法)。

改写: 要考虑的另一件事是使用业务对象来表示集合/表中的数据。 ORM(对象关系映射器)可以为您处理这个问题,但它们是大型且功能强大的框架,不容易掌握。它与在 UI 层使用 DataSet/Tables 是完全不同的范例,并且更适用于面向对象的编程。 DataSets 和 Tables 非常适合处理表格数据,但它们不适合处理实体。例如,您将针对具有 IsHipanicIsCitizen rahtner 等属性的 Person 对象的实例进行操作,而不是对表格中的单元格进行操作(不再有 *myPersonTable[0]["HisponicOriginFlag"]....)。

进一步:与您的问题无关,但与围绕 ADO.NET 的 CRUD 操作相关:熟悉内置在 DataTable/DataSet 中的状态跟踪是值得的。 ADO.NET 中有很多内置功能可以帮助将这些应用程序轻松粘合在一起,这将像您展示的那样清理 代码。

与往常一样,RAD 工具需要权衡放弃对生产力的控制——但在不理解它们的情况下将它们写下来可以保证你会花你的时间编写代码,就像你展示的那样。

更多:为了进一步完善我之前的Further,当您发现将 Visual Studio 的 DataSet 生成器与 DataTables 的内置行状态跟踪相结合的能力时,以及数据集的变化跟踪可以很容易地在很短的时间内编写一个完整的 CRUD 系统。

以下是所涉及的一些步骤的简要说明:

    建立您的数据库架构 在 Visual Studio 中将新的 DataSet 项添加到项目中 找到服务器资源管理器(在视图下) 将您的 SQL Server 添加为数据连接 将您的表/存储过程/视图拖到数据集的设计器中。 右键单击Visual Studio为您生成的“TableAdapter”;转到配置 为数据集配置 CRUD 命令(选择、插入、更新、删除命令)

您已经创建了一个强类型数据集。 DataSet 将包含一个DataTable 属性,该属性以用于生成DataSet 的表/视图/存储过程命名。该 Table 属性将包含强类型行,这使您可以将该行中的单元格作为属性而不是对象数组中的无类型项来访问。

因此,如果您生成了一个名为 MyDbTables 的新数据集,其中包含一个名为 tblCustomer 的表,其中包含一些列,例如 CustomerId名称等...然后你可以像这样使用它:

这是一个多种示例,展示了用于 CRUD 工作的一些常用方法——查看方法,特别是 TableAdapter 类

    public void MyDtDemo()
    
        // A TableAdapter is used to perform the CRUD operations to sync the DataSet/Table and Database
        var myTa = new ClassLibrary4.MyDbTablesTableAdapters.tblCustomersTableAdapter();
        var myDataSet = new MyDbTables();

        // 'Fill' will execute the TableAdapter's SELECT command to populate the DataTable
        myTa.Fill(myDataSet.tblCustomers);

        // Create a new Customer, and add him to the tblCustomers table
        var newCustomer = myDataSet.tblCustomers.NewtblCustomersRow();
        newCustomer.Name = "John Smith";
        myDataSet.tblCustomers.AddtblCustomersRow(newCustomer);

        // Show the pending changes in the DataTable
        var myTableChanges = myDataSet.tblCustomers.GetChanges();

        // Or get the changes by change-state
        var myNewCustomers = myDataSet.tblCustomers.GetChanges(System.Data.DataRowState.Added);

        // Cancel the changes (if you don't want to commit them)
        myDataSet.tblCustomers.RejectChanges();

        // - Or Commit them back to the Database using the TableAdapter again
        myTa.Update(myDataSet);
    

另外,请注意 DataSets 和 DataTables 的 RejectChanges() 和 AcceptChanges() 方法。他们基本上告诉您的数据集它没有更改(通过拒绝所有更改或“提交”所有更改),但请注意调用 AcceptChanges() 然后尝试进行更新将没有效果 - DataSet 已经丢失跟踪任何更改并假定它是数据库的准确反映。

还有更多!这是您示例的修改版本,其中显示了一些行状态跟踪功能,假设您已按照我的步骤创建强类型数据集/表/行

    public void CheckRows()
    
       MyPersonDS tmpPersonDS = new MyPersonDS();

        // Load Person info
       using (var tmpPersonDT = tmpPersonDS.PersonDT)
       
           foreach (MyPersonRow row in tmpPersonDT.Rows)
           
               CheckPersonData(row);
           
       

    

    public void CheckPersonData(MyPersonRow row)
    
        // If DataBinding is used, then show if the row is unchanged / modified / new...
        System.Diagnostics.Debug.WriteLine("Row State: " + row.RowState.ToString());

        System.Diagnostics.Debug.WriteLine("Row Changes:");
        System.Diagnostics.Debug.WriteLine(BuildRowChangeSummary(row));

        // If not DataBound then update the strongly-types Row properties
        row.ResidencyCountyID = lkuResidencyCountyId.EditValue;


    

    public string BuildRowChangeSummary(DataRow row)
    
        System.Text.StringBuilder result = new System.Text.StringBuilder();

        int rowColumnCount = row.Table.Columns.Count;
        for (int index = 0; index < rowColumnCount; ++index)
        
            result.Append(string.Format("Original value of 0: 1\r\n", row.Table.Columns[index].ColumnName, row[index, DataRowVersion.Original]));
            result.Append(string.Format("Current  value of 0: 1\r\n", row.Table.Columns[index].ColumnName, row[index, DataRowVersion.Current]));

            if (index < rowColumnCount - 1)  result.Append("\r\n"); 
        

        return result.ToString();
    

【讨论】:

查看我继承这些东西的开发人员告诉我要远离 MS 的数据绑定...... 你应该给那些开发者一封讨厌的信。 @Refracted:你不得不继承他们的代码,这(一目了然)闻起来很臭。不要在不质疑他们的理由的情况下继承他们的心态 我敢打赌,这些都是老 VB6 程序员告诉你的。他们可能永远不会知道微软在 VB.NET 1.0 中修复了多少 VB6 的问题。 @Refracted:你会准确地停在“VB6 中存在重大问题”:)【参考方案2】:

如果您对映射到数据集列名称的属性使用一致的命名约定,则反射将使单一可重用方法成为可能。但是,您需要对其进行测试以确保它不会导致性能问题。

为此,循环遍历表中的 Columns 集合。然后,对于每一列,使用反射来查找与行进行比较的对象的“匹配”属性。 (您需要关注从 obj.GetType().GetProperties(...) 返回的 PropertyInfo 类。)

【讨论】:

【参考方案3】:

数据行还会自动跟踪更改。

DataRow _personInfo = ConnectBLL.BLL.Person.GetPerson(personID);  
// _personInfo.RowState = DataRowState.Unchanged
_personInfo["columnName"] = "value";
_personInfo["columnName2"] = "value2";
_personInfo.EndEdit();
// _personInfo.RowState = DataRowState.Modified

现在您可以通过询问行状态和检查值来获取更改的值,如下所示

var org = fRow["columnName", DataRowVersion.Original]; 
var new = fRow["columnName",DataRowVersion.Current];

要检测更改,您还可以侦听 columnchanged 事件。

fTable.ColumnChanged += new DataColumnChangeEventHandler(delegate(object sender, DataColumnChangeEventArgs e)

   Console.WriteLine(e.Column.ColumnName);

);

【讨论】:

我是怎么更新这个的!!!我仍然需要提交更改(到数据库),对吗?我想我得到的是_personInfo.AcceptChanges()_personInfo.BeginEdit()在哪里发挥作用;如果有的话? BeginEdit() 强制该行进入“编辑模式”。我想这也是您更改值的时候。 AcceptChanges 当您已将更改提交到数据库并想要重新开始时。 (原始值变为当前值)。

以上是关于使用数据集的更有效方式的主要内容,如果未能解决你的问题,请参考以下文章

基于多个字段搜索大型数据集的有效方法

使用 fitctree 训练具有不平衡训练集的更敏感模型

在 R 中拆分大型数据集的有效方法

(预)处理存储在 json 中的大型数据集的最有效方法是啥?

在 Java Spark 中迭代大型数据集的最快且有效的方法

从大型数据集的数据框有效地创建矩阵