使用数据集的更高效方法

4
我有以下代码,每个表单都会重复使用作为更新过程的一部分。当页面加载时,BLL会返回一个DataSet,比如说:
_personInfo = ConnectBLL.BLL.Person.GetPerson(personID);

我将那个DataSet存储在一个表单级变量中,然后在验证/更新过程中用它来检查更改。我逐行传递数据(尽管从未超过一行)到一个函数中,该函数获取控件中的值并将其与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应用程序中使用数据集/数据表时,我是否遗漏了一些明显的东西?


juliandewitt的下面的帖子很棒!

现在我需要一些关于如何在上述内容中使用它的方向。任何人可以指向的链接都将不胜感激。如果您能发布一个示例,那就更好了。

使用DataRows是否存在任何缺点?


哇,我想这可能是我回答最长的问题了 :) 但说真的,我希望通过内置状态跟踪、强类型数据集和表适配器以及数据绑定,我们已经为您打开了一些在Visual Studio中找到RAD工具以及ADO.NET的强大功能的门。话虽如此,它并不是万能的,也不是一个完美的系统;它是一个广泛的系统,有其缺陷和局限性——但它是.NET 1.0-2.0的基石。然而,.NET 3.0及以上版本开始偏离ADO.NET,在3.5 SP1中引入了实体框架。但这仍然值得了解! - STW
3个回答

7
看起来你正在进行很多手动工作,可以通过将控件直接绑定到数据集/表来减轻负担。数据绑定将数据源(在这种情况下是数据集/表)与用户界面连接在一起。当用户界面的值发生更改时,它将更新数据源。
数据绑定是一个重要的话题,需要进行研究和测试。使用数据绑定到DataTable/Set有一些要注意的地方(行更改不会提交,直到当前行更改,这在你只处理单个行的情况下很麻烦,但有解决方法)。
另一个要考虑的事情是使用业务对象来表示Set/Table中的数据。ORM(对象关系映射器)可以为您处理此操作,但它们是庞大且功能强大的框架,不易于掌握。这与在UI层使用DataSet/Table完全不同,并且更符合面向对象编程的思想。DataSet和Table非常适合处理表格数据,但它们并不太适合处理实体。例如,您将针对具有像IsHispanic和IsCitizen这样的属性的Person对象实例进行操作,而不是基本上针对表中的单元格进行操作(不再是*myPersonTable [0] ["HispanicOriginFlag"]...)。
与您的问题无关,但与围绕ADO.NET的CRUD操作相关的是,熟悉DataTable/DataSet中内置的状态跟踪非常重要。ADO.NET中有很多内容可帮助轻松地将这些应用程序粘在一起,这将清理掉像您展示的那样的大量代码。
像始终如一的RAD工具一样,放弃控制以换取生产力的权衡-但是如果不了解它们就写它们,那么你将保证会花费整天编写像你展示的那样的代码。
进一步地,当您发现Visual Studio的DataSet生成器与DataTable的内置行状态跟踪以及DataSet的更改跟踪相结合的能力时,可以很容易地在短时间内编写完整的CRUD系统。
以下是一些步骤的快速概述:
1.建立数据库架构 2.在Visual Studio中向项目添加新的DataSet项 3.找到Server Explorer(在视图下) 4.将SQL Server添加为数据连接 5.将表/存储过程/视图拖到DataSet的设计器中。 6.右键单击Visual Studio为您生成的“TableAdapter”,转到配置 7.配置DataSet的CRUD命令(选择、插入、更新、删除命令)
使用这种方法,您创建了一个强类型的DataSet。 DataSet将包含一个DataTable属性,该属性以生成DataSet的表/视图/存储过程命名。该Table属性将包含强类型行,这使您可以将该行中的单元格作为属性访问,而不是作为对象数组中的未经分类的项目。
因此,如果您生成了一个名为MyDbTables的新DataSet,其中包含一个名为tblCustomer的表,该表包含一些列,例如CustomerIdName等,则可以按如下方式使用它: 这是各种示例的综合体,展示了用于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 已经失去了对任何更改的追踪,并假设它是数据库的准确反映。
更棒的是!这里有一个重新制作的示例,展示了一些行状态跟踪功能,假设您已经按照我的步骤创建了强类型 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();
    }

我继承这些东西的开发人员告诉我要远离微软的数据绑定... - Refracted Paladin
1
你应该给那些开发者发送一封不友善的信。 - John Saunders
1
@Refracted:你不得不继承他们的代码,但是(乍一看)它有点臭。不要在没有质疑他们的原因的情况下继承他们的思维方式。 - STW
1
我敢打赌这些人是老的VB6程序员,他们可能从未了解过微软在VB.NET 1.0中修复了多少VB6的问题。 - John Saunders
1
@Refracted:你说“VB6存在重大问题”已经很准确了 :) - STW
显示剩余6条评论

2
此外,数据行会自动跟踪更改。
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()在哪里发挥作用呢?如果有的话? - Refracted Paladin
调用BeginEdit()方法可以将行强制进入“编辑模式”。我猜这也是在更改值时发生的。<br> 当您已经提交了对数据库的更改并想重新开始时,请调用AcceptChanges方法。(原始值变为当前值)。 - Julian de Wit

2
如果您在属性命名方面使用一致的约定,以映射到数据集列名称,那么反射将使单个可重用方法成为可能。但是,您需要测试它以确保它不会导致性能问题。
要做到这一点,请循环遍历表中的Columns集合。然后,对于每个列,使用反射在与行进行比较的对象上找到“匹配”的属性。(您需要关注从obj.GetType().GetProperties(...)返回的PropertyInfo类。)

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