用 C# 與 SP 撰寫簡易結算程式
最近研究結算程式,發現要大量的更新資料,還是得寫資料庫的SP,於是就研究了寫法,也順便把整個流程寫成小程式,程式碼如下:
public class SettlementManager
{
public static SettlementManager Instance = new SettlementManager();
private SettlementLib _settlementLib;
private SettlementManager()
{
_settlementLib = new SettlementLib();
_sqlConnection = new SqlConnection(SQLConnectionString);
}
private SqlConnection _sqlConnection = null;
public static string SQLConnectionString;
public async Task StartFlow()
{
_sqlConnection.ConnectionString = SQLConnectionString;
_sqlConnection.Open();
var list = await _settlementLib.PullDatas(_sqlConnection);
await _settlementLib.HandleStockList(list);
await _settlementLib.UpdateWinloss();
Task.WaitAll();
await _settlementLib.UpdateDatas(_sqlConnection);
_sqlConnection.Close();
}
}
上面的類別只有流程,實際的執行方式如下:
public class SettlementLib
{
private ConcurrentDictionary<string, decimal> _memberWinloss;
private ConcurrentQueue<Stock> _queue;
private bool _continue = false;
public SettlementLib()
{
_memberWinloss = new ConcurrentDictionary<string, decimal>();
_queue = new ConcurrentQueue<Stock>();
}
public async Task UpdateDatas(SqlConnection sqlConnection)
{
string tableName = "StockTemp";
List<TempStockData> data = new List<TempStockData>();
foreach (var item in _memberWinloss)
{
TempStockData tempStockData = new TempStockData
{
Id = item.Key,
Name = item.Key,
Amount = 0,
Winloss = item.Value
};
data.Add(tempStockData);
}
var table = DBTool.CreateDataTable(data);
DBTool.SqlBulkCopy(sqlConnection, table, tableName);
//exec db SP
var result = await sqlConnection.QueryAsync("dbo.UpdateWinlossDatasToUsers", commandType: CommandType.StoredProcedure);
}
public async Task UpdateWinloss()
{
bool flag = true;
while (flag)
{
if (_queue.TryDequeue(out Stock stock))
{
if(!_memberWinloss.ContainsKey(stock.Id))
{
_memberWinloss.TryAdd(stock.Id, 0);
}
_memberWinloss[stock.Id] += stock.Winloss;
}
else
{
flag = false;
}
}
await Task.Delay(100);
}
public async Task HandleStockList(List<Stock> stocks)
{
Parallel.ForEach(stocks, stock =>
{
if (stock.IsWin)
{
stock.Winloss = (decimal)stock.Odd * stock.Amount;
}
_queue.Enqueue(stock);
});
await Task.Delay(100);
_continue = false;
}
public async Task<List<Stock>> PullDatas(SqlConnection sqlConnection)
{
var data = new List<Stock>();
//exec db SP
var result = await sqlConnection.QueryAsync<Stock>("dbo.PullUnsettleStocks", commandType: CommandType.StoredProcedure);
foreach (Stock stock in result)
{
data.Add(stock);
}
return data;
}
最後是更新用的SP:
USE [BulkCopyDemo]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[UpdateWinlossDatasToUsers]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
UPDATE [BulkCopyDemo].[dbo].[Member]
SET
Credit = Credit + Stock.Winloss
FROM [BulkCopyDemo].[dbo].[Member]
INNER JOIN [BulkCopyDemo].[dbo].[StockTemp] Stock
ON Stock.Id = [BulkCopyDemo].[dbo].[Member].Id
TRUNCATE TABLE [BulkCopyDemo].[dbo].[StockTemp]
END
流程就是:
- 執行 PullDatas 抓取未結算資料
- 執行 HandleStockList 結算金額並放進 queue
- 執行 UpdateWinloss 把結算金額更新回會員的錢包
- 執行 UpdateDatas 利用 SP 大量更新資料
完整程式碼請參考下列連結:
參考資料