我试图将一些旧的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);
}
}
注意:我是公司的新手并且不能告诉他们必须更改连接字符串或必须切换到实体框架,这将是我的选择。我真的在寻找仅限代码的解决方案。
答案 0 :(得分:6)
ADO.NET DataTable
和DataAdapter
提供最接近的等效ADO Recordset
,并应用了concens原则的分离。 DataTable
包含数据并提供更改跟踪信息(类似于EF内部实体跟踪),而DataAdapter
提供了从数据库(Fill
方法)填充更改的标准方法,并将更改应用于数据库(Update
方法)。
话虽如此,你在做什么是将ADO Recordset
移植到ADO.NET的预期方法。您唯一遗漏的是,您并不总是需要指定Insert
,Update
和Delete
命令。一旦您的查询查询单个表(我认为这是获得可更新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
事件对同一行的多次更改的行为方式。最坏的情况是,对于行中的每个更改都会被触发。
类构造函数有三个参数:
RowChanged
班级的实例。DataTable
Dictionary<string, SqlDbType>
的{{1}}属性。获得映射字典后,您所要做的就是实例化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);
}
}
我模拟了该解决方案,并且它具有高性能,因为所有更新/删除都是批量运行。