更新或删除当前的数据库结果行

时间:2018-03-15 22:50:25

标签: c# .net vb6 ado.net sqldataadapter

我试图将一些旧的VB6代码移植到C#和.NET。

旧代码使用RecordSet执行SQL查询然后遍历结果有很多地方。到目前为止没问题,但在循环内部代码对当前行进行更改,更新列甚至完全删除当前行。

在.NET中,我可以轻松地使用SqlDataReader循环访问SQL查询结果,但不支持更新。

所以我一直在玩SqlDataAdapter来填充DataSet,然后循环遍历DataSet表格中的行。但与{2006}的DataSet相比,RecordSet看起来并不聪明。首先,我需要为每种类型的编辑提供更新查询。另一个问题是DataSet似乎同时将所有内容保存在内存中,如果有很多结果,这可能会成为一个问题。

在.NET中复制此行为的最佳方法是什么?下面的代码显示了我到目前为止的内容。这是最好的方法,还是有其他选择?

using (SqlConnection connection = new SqlConnection(connectionString))
{
    DataSet dataset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(new SqlCommand(query, connection)))
    {
        adapter.Fill(dataset);

        DataTable table = dataset.Tables[0];
        foreach (DataRow row in table.Rows)
        {
            if ((int)row["Id"] == 4)
            {
                if ((int)row["Value1"] > 0)
                    row["Value2"] = 12345;
                else
                    row["Value3"] = 12345;
            }
            else if ((int)row["Id"] == 5)
            {
                 row.Delete();
            }
        }

        // TODO:
        adapter.UpdateCommand = new SqlCommand("?", connection);
        adapter.DeleteCommand = new SqlCommand("?", connection);

        adapter.Update(table);
    }
}

注意:我是公司的新手并且不能告诉他们必须更改连接字符串或必须切换到实体框架,这将是我的选择。我真的在寻找仅限代码的解决方案。

3 个答案:

答案 0 :(得分:6)

ADO.NET DataTableDataAdapter提供最接近的等效ADO Recordset,并应用了concens原则的分离。 DataTable包含数据并提供更改跟踪信息(类似于EF内部实体跟踪),而DataAdapter提供了从数据库(Fill方法)填充更改的标准方法,并将更改应用于数据库(Update方法)。

话虽如此,你在做什么是将ADO Recordset移植到ADO.NET的预期方法。您唯一遗漏的是,您并不总是需要指定InsertUpdateDelete命令。一旦您的查询查询单个表(我认为这是获得可更新Recordset的要求),您可以使用另一个名为DbCommandBuilder的ADO.NET播放器:

  

自动生成单表命令,用于协调对DataSet所做的更改与关联数据库的对比。

每个数据库提供程序都提供此抽象类的实现。 MSDN example for SqlCommandBuilder几乎与您的示例完全相同,因此在调用Update之前所需要的只是(有点违反直觉):

var builder = new SqlCommandBuilder(adapter); 

就是这样。

在幕后,

  

DbCommandBuilder将自身注册为RowUpdating事件的侦听器,该事件由此属性中指定的DbDataAdapter生成。

如果没有在数据适配器中专门设置命令,请

动态生成命令。

答案 1 :(得分:1)

我想出了一个(未经测试的)数据表解决方案。 它确实需要您做一些工作,但它应该为您自动更改或删除的每一行生成更新和删除命令,方法是连接到RowChanged的{​​{1}}和RowDeleted事件

每一行都会获得它自己的命令,相当于DataTable更新/删除方法。

但是,与ADODB.RecordSet方法不同,此类不会更改underling数据库,只会创建SqlCommands来执行此操作。当然,您可以将其更改为在创建后立即执行它们,但正如我所说,我没有对其进行测试,因此如果您愿意,我会将其保留给您。但请注意,我不确定ADODB.RecordSet事件对同一行的多次更改的行为方式。最坏的情况是,对于行中的每个更改都会被触发。

类构造函数有三个参数:

  1. 您正在使用的RowChanged班级的实例。
  2. 提供列名与SqlDataTypes
  3. 之间映射的DataTable
  4. 表示表名的可选字符串。如果省略,将使用Dictionary<string, SqlDbType>的{​​{1}}属性。
  5. 获得映射字典后,您所要做的就是实例化TableName类并迭代数据表中的行,就像在问题中一样。从那时起,一切都是自动化的。

    完成迭代后,您所要做的就是从DataTable属性获取sql命令,然后运行它们。

    CommandGenerator

    正如我所写的那样,这是完全未经测试的,但我认为至少应该表明一般的想法应该足够了。

答案 2 :(得分:0)

您的约束:

  • 不使用实体框架

  • DataSet似乎同时将所有内容保存在内存中,这可能是一个 如果有很多结果会有问题。

  • 仅限代码的解决方案(无外部库)

<强>加

  • DataTable可以存储的最大行数为16,777,216 行MSDN

  • 获得高性能

    //the main class to update/delete sql batches without using DataSet/DataTable.
    
    
    public class SqlBatchUpdate
        {
            string ConnectionString { get; set; }
            public SqlBatchUpdate(string connstring)
            {
                ConnectionString = connstring;
            }
    
            public int RunSql(string sql)
            {
                using (SqlConnection con = new SqlConnection(ConnectionString))
                using (SqlCommand cmd = new SqlCommand(sql, con))
                {
                    cmd.CommandType = CommandType.Text;
                    con.Open();
                    int rowsAffected = cmd.ExecuteNonQuery();
                    return rowsAffected;
                }
            }
        }
    
    //------------------------
    // using the class to run a predefined patches
    
     public class SqlBatchUpdateDemo
        {       
           private string connstring = "myconnstring";
    
           //run batches in sequence
        public void RunBatchesInSequence()
        {
            var sqlBatchUpdate = new SqlBatchUpdate(connstring);
    
            //batch1
            var sql1 = @"update mytable set value2 =1234 where id =4  and Value1>0;";
            var nrows = sqlBatchUpdate.RunSql(sql1);
            Console.WriteLine("batch1: {0}", nrows);
    
            //batch2
            var sql2 = @"update mytable set value3 =1234 where      id =4   and Value1 =0";
              nrows = sqlBatchUpdate.RunSql(sql2);
            Console.WriteLine("batch2: {0}", nrows);
    
            //batch3
            var sql3 = @"delete from  mytable where id =5;";
              nrows = sqlBatchUpdate.RunSql(sql3);
            Console.WriteLine("batch3: {0}", nrows);
    
    
        }
    
            // Alternative: you can run all batches as one 
                public void RunAllBatches()
                {
                    var sqlBatchUpdate = new SqlBatchUpdate(connstring );
                    StringBuilder sb = new StringBuilder();
                    var sql1 = @"update mytable set value2 =1234 where id =4  and Value1>0;";
                    sb.AppendLine(sql1);
    
                    //batch2
                    var  sql2 = @"update mytable set value3 =1234 where     id =4   and Value1 =0";
                    sb.AppendLine(sql2);
    
                    //batch3
                   var sql3 = @"delete from  mytable where  id =5;";
                    sb.AppendLine(sql3);
    
                    //run all batches
                    var   nrows = c.RunSql(sb.ToString());
                    Console.WriteLine("all patches: {0}", nrows);
                }
    
    
        }
    

我模拟了该解决方案,并且它具有高性能,因为所有更新/删除都是批量运行。