Dapper操作SQL Server
平时使用dapper直接面向SQL比较多,这里做个总结。
环境基于.NET Core 3.1,开设2个程序同时做插入、更新等操作,以及理解事务的过程。
插入
数据库代码:
public void Insert(string sql, object args)
{
try
{
using var connection = new SqlConnection(_connStr);
if(connection.State == System.Data.ConnectionState.Closed) connection.Open();
connection.Execute(sql, args);
}catch(Exception ex)
{
throw ex;
}
}
测试所需的数据表与代码:

//插入数据
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var baseDate = new DateTime(2022, 11, 17, 0, 0, 0, 0);
Task.Factory.StartNew(() => {
while (true)
{
_manualResetEvent.WaitOne();
var msg = $"App1: {(DateTime.Now - baseDate).TotalMilliseconds}{Environment.NewLine}";
this.Dispatcher.Invoke(_handleLogMsg, msg);
var sqls = @"insert into KO_A(numbers) values(1);";
var args = new { DT = DateTime.Now };
try
{
new DBHelper().Insert(sqls, null);
}
catch (Exception ex)
{
this.Dispatcher.Invoke(_handleLogMsg, ex.Message);
}
}
});
}
ManualResetEvent _manualResetEvent = new ManualResetEvent(true);
//暂停插入
private void Button_Click_2(object sender, RoutedEventArgs e)
{
_manualResetEvent.Reset();
}
APP2中的测试代码相同,同时执行一段时间后点击暂停。
测算UI发起时间到入库时间,为了方便统计,每行执行时间减去基准时间,算总的毫秒数。2应用下平均时间在200~300毫秒,

多方读更数据
主要考虑实现幂等更新。这里,有2个app同时更新一张表的Numbers字段,每次更新伴随着version的叠加。version版本号的引入降低了:库存是否溢出的判断逻辑。
数据表:

数据操作:
public int Update(string sql, object args)
{
int lines = -1;
try
{
using var connection = new SqlConnection(_connStr);
if (connection.State == System.Data.ConnectionState.Closed) connection.Open();
using (var trans = connection.BeginTransaction())
{
lines = connection.Execute(sql, args, trans);
//Thread.Sleep(1000);//延长模拟时间
trans.Commit();
}
return lines;
}
catch (Exception ex)
{
throw ex;
}
}
public IEnumerable<T> Get<T>(string sql, object args) where T : new()
{
try
{
using var connection = new SqlConnection(_connStr);
if (connection.State == System.Data.ConnectionState.Closed) connection.Open();
return connection.Query<T>(sql, args);
}
catch (Exception ex)
{
throw ex;
}
}
APP1和APP2的代码相同,只是在读取存货后,APP2会停顿0.2s再请求做更新,以验证幂等更新。
业务层代码:
public class KOBHandle
{
public KO_B Get()
{
var sqls = @"select * from KO_B;";
var res = new DBHelper().Get<KO_B>(sqls, null).FirstOrDefault();
return res;
}
public int Update(KO_B data)
{
var sqls = @"update KO_B set Numbers = @Num, version = version + 1 where version = @Ver;";
return new DBHelper().Update(sqls, new { Num = data.Numbers, Ver = data.Version});
}
}
UI代码:
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var baseDate = new DateTime(2022, 11, 17, 0, 0, 0, 0);
Task.Factory.StartNew(() =>
{
while (true)
{
_manualResetEvent.WaitOne();
try
{
Thread.Sleep(200);
var bl = new KOBHandle();
var res = bl.Get(); //UI获取的商品存货
if (res == null) return;
if (res.Numbers <= 0)
{
this.Dispatcher.Invoke(_handleLogMsg, "存库空.");
return;
}
var temp = res.Numbers;
var temp2 = new Random().Next(1, res.Numbers);
res.Numbers = res.Numbers -temp2;//随机出库若干
var rtn = new KOBHandle().Update(res);
var msg = $"App1: 存货:{temp} 出货:{temp2} 更新:{rtn} {res.Version}{Environment.NewLine}";
this.Dispatcher.Invoke(_handleLogMsg, msg);
}
catch (Exception ex)
{
this.Dispatcher.Invoke(_handleLogMsg, ex.Message);
}
}
});
}
rtn 1 表示更新成功,0表示未更新;
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var baseDate = new DateTime(2022,11,17,0,0,0,0);
Task.Factory.StartNew(()=> {
while (true)
{
_manualResetEvent.WaitOne();
//Thread.Sleep(1000);
try
{
var bl = new KOBHandle();
var res = bl.Get(); //UI获取的商品存货
if (res == null) return;
if (res.Numbers <= 0)
{
this.Dispatcher.Invoke(_handleLogMsg, "存库空.");
return;
}
Thread.Sleep(200);//停顿200ms
var temp = res.Numbers;
var temp2 = new Random().Next(1, res.Numbers);
res.Numbers = res.Numbers - temp2;//随机出库若干
var rtn = new KOBHandle().Update(res);
var msg = $"App2: 存货:{temp} 出货:{temp2} 更新:{rtn} {res.Version}{Environment.NewLine}";
this.Dispatcher.Invoke(_handleLogMsg, msg);
}
catch (Exception ex)
{
this.Dispatcher.Invoke(_handleLogMsg, ex.Message);
}
}
});
}
测试结果:


1134为当前行数据版本号,存货3,APP1和APP2一致;APP1更新0表示失败,APP2更新1表示更新成功。
同理分析1136,APP1更新成功;APP2更新不成功。
浙公网安备 33010602011771号