C# 更新或删除数据库结果的当前行
我正在尝试将一些旧的VB6代码移植到C#和.NET 在许多地方,旧代码使用C# 更新或删除数据库结果的当前行,c#,.net,vb6,ado.net,sqldataadapter,C#,.net,Vb6,Ado.net,Sqldataadapter,我正在尝试将一些旧的VB6代码移植到C#和.NET 在许多地方,旧代码使用记录集执行SQL查询,然后循环遍历结果。到目前为止没有问题,但是在循环中,代码对当前行进行更改,更新列,甚至删除当前行 在.NET中,我可以轻松地使用SqlDataReader循环查询SQL查询结果,但不支持更新 因此,我一直在使用SqlDataAdapter填充数据集,然后循环遍历数据集表中的行。但是数据集与VB6的旧记录集相比似乎不是很智能。首先,我需要为每种类型的编辑提供更新查询。另一个问题是,数据集似乎同时保存内存
记录集执行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.NETDataTable
和DataAdapter
提供了ADORecordset
最接近的等价物,并应用了概念分离原则<
var builder = new SqlCommandBuilder(adapter);