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

C# 更新或删除数据库结果的当前行,c#,.net,vb6,ado.net,sqldataadapter,C#,.net,Vb6,Ado.net,Sqldataadapter,我正在尝试将一些旧的VB6代码移植到C#和.NET 在许多地方,旧代码使用记录集执行SQL查询,然后循环遍历结果。到目前为止没有问题,但是在循环中,代码对当前行进行更改,更新列,甚至删除当前行 在.NET中,我可以轻松地使用SqlDataReader循环查询SQL查询结果,但不支持更新 因此,我一直在使用SqlDataAdapter填充数据集,然后循环遍历数据集表中的行。但是数据集与VB6的旧记录集相比似乎不是很智能。首先,我需要为每种类型的编辑提供更新查询。另一个问题是,数据集似乎同时保存内存

我正在尝试将一些旧的VB6代码移植到C#和.NET

在许多地方,旧代码使用
记录集执行SQL查询,然后循环遍历结果。到目前为止没有问题,但是在循环中,代码对当前行进行更改,更新列,甚至删除当前行

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

因此,我一直在使用
SqlDataAdapter
填充
数据集
,然后循环遍历
数据集
表中的行。但是
数据集
与VB6的旧
记录集
相比似乎不是很智能。首先,我需要为每种类型的编辑提供更新查询。另一个问题是,
数据集
似乎同时保存内存中的所有内容,如果有许多结果,这可能是一个问题

在.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);
    }
}

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

您的限制:

  • 不使用实体框架

  • 数据集似乎同时保存了内存中的所有内容,这可能是一个错误 问题是如果有很多结果

  • 纯代码解决方案(无外部库)

Plus

  • 数据表可以存储的最大行数为16777216 划船

  • 获得高性能

    //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);
                }
    
    
        }
    
我模拟了该解决方案,它运行良好,性能很高,因为所有更新/删除都以批处理方式运行。

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

每行将获得自己的命令,相当于
ADODB.RecordSet
update/delete方法

但是,与
ADODB.RecordSet
方法不同,该类不会更改下划线数据库,而只创建执行该操作的SqlCommands。当然,您可以将其更改为在创建它们之后简单地执行它们,但是正如我所说的,我没有测试它,所以如果您想这样做,我将留给您。但是,请注意,我不确定
RowChanged
事件在对同一行进行多次更改时的行为。最坏的情况是,该行中的每一次更改都将触发该命令

类构造函数有三个参数:

  • 正在使用的
    DataTable
    类的实例
  • 提供列名和sqldatatype之间映射的
    字典
  • 表示表名的可选字符串。如果省略,将使用
    DataTable
    TableName
    属性
  • 一旦有了映射字典,您所要做的就是实例化
    CommandGenerator
    类,并像问题中一样迭代数据表中的行。从那时起,一切都是自动化的

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

    public class CommandGenerator
    {
        private Dictionary<string, SqlDbType> _columnToDbType;
        private string _tableName;
        private List<SqlCommand> _commands;
    
        public CommandGenerator(DataTable table, Dictionary<string, SqlDbType> columnToDbType, string tableName = null)
        {
            _commands = new List<SqlCommand>();
            _columnToDbType = columnToDbType;
            _tableName = (string.IsNullOrEmpty(tableName)) ? tableName : table.TableName;
    
            table.RowDeleted += table_RowDeleted;
            table.RowChanged += table_RowChanged;
        }
    
        public IEnumerable<SqlCommand> Commands { get { return _commands; } }
    
        private void table_RowChanged(object sender, DataRowChangeEventArgs e)
        {
            _commands.Add(GenerateDelete(e.Row));
        }
    
        private void table_RowDeleted(object sender, DataRowChangeEventArgs e)
        {
            _commands.Add(GenerateDelete(e.Row));
        }
    
        private SqlCommand GenerateUpdate(DataRow row)
        {
    
            var table = row.Table;
            var cmd = new SqlCommand();
            var sb = new StringBuilder();
            sb.Append("UPDATE ").Append(_tableName).Append(" SET ");
    
            var valueColumns = table.Columns.OfType<DataColumn>().Where(c => !table.PrimaryKey.Contains(c));
    
            AppendColumns(cmd, sb, valueColumns, row);
            sb.Append(" WHERE ");
            AppendColumns(cmd, sb, table.PrimaryKey, row);
    
            cmd.CommandText = sb.ToString();
            return cmd;
        }
    
        private SqlCommand GenerateDelete(DataRow row)
        {
            var table = row.Table;
            var cmd = new SqlCommand();
            var sb = new StringBuilder();
            sb.Append("DELETE FROM ").Append(_tableName).Append(" WHERE ");
            AppendColumns(cmd, sb, table.PrimaryKey, row);
            cmd.CommandText = sb.ToString();
            return cmd;
        }
    
        private void AppendColumns(SqlCommand cmd, StringBuilder sb, IEnumerable<DataColumn> columns, DataRow row)
        {
            foreach (var column in columns)
            {
                sb.Append(column.ColumnName).Append(" = @").AppendLine(column.ColumnName);
                cmd.Parameters.Add("@" + column.ColumnName, _columnToDbType[column.ColumnName]).Value = row[column];
            }
        }
    }
    
    公共类命令生成器
    {
    专用词典_columnToDbType;
    私有字符串_tableName;
    私有列表命令;
    公共命令生成器(DataTable表,Dictionary columnToDbType,string tableName=null)
    {
    _commands=newlist();
    _columnToDbType=columnToDbType;
    _tableName=(string.IsNullOrEmpty(tableName))?tableName:table.tableName;
    table.RowDeleted+=表_RowDeleted;
    table.RowChanged+=table_RowChanged;
    }
    公共IEnumerable命令{get{return\u Commands;}}
    私有无效表_RowChanged(对象发送方,DataRowChangeEventArgs e)
    {
    _commands.Add(GenerateDelete(e.Row));
    }
    私有无效表_行已删除(对象发送方,DataRowChangeEventArgs e)
    {
    _commands.Add(GenerateDelete(e.Row));
    }
    专用SqlCommand GenerateUpdate(数据行)
    {
    var table=row.table;
    var cmd=new SqlCommand();
    var sb=新的StringBuilder();
    追加(更新)追加(_tableName)。追加(SET);
    var valueColumns=table.Columns.OfType()。其中(c=>!table.PrimaryKey.Contains(c));
    追加列(cmd、sb、valueColumns、row);
    (b)附加(“何处”);
    追加列(cmd、sb、table.PrimaryKey、row);
    cmd.CommandText=sb.ToString();
    返回cmd;
    }
    专用SqlCommand GenerateDelete(数据行)
    {
    var table=row.table;
    var cmd=new SqlCommand();
    var sb=新的StringBuilder();
    附加(表名)附加(WHERE);
    追加列(cmd、sb、table.PrimaryKey、row);
    cmd.CommandText=sb.ToString();
    返回cmd;
    }
    私有列(SqlCommand cmd、StringBuilder sb、IEnumerable列、DataRow行)
    {
    foreach(列中的var列)
    {
    sb.Append(column.ColumnName).Append(=@”).AppendLine(column.ColumnName);
    cmd.Parameters.Add(“@”+column.ColumnName,_columnToDbType[column.ColumnName])。Value=row[column];
    }
    }
    }
    

    正如我所写的,这是完全未经测试的,但我认为它至少应该足以说明总体思路。

    ADO.NET
    DataTable
    DataAdapter
    提供了ADO
    Recordset
    最接近的等价物,并应用了概念分离原则<
    var builder = new SqlCommandBuilder(adapter);