我在asp.net core myweb api项目中使用Dapper(https://github.com/StackExchange/Dapper)。我需要创建一个主记录并将一组行插入子表。
Ex. OrdersMaster table
create an order id in this table
OrderDetails table
ordermasterid
order items
我怎么能用Dapper做到这一点?如果可能,请分享一些代码段。
答案 0 :(得分:2)
我将插入操作实现为事务存储过程,然后从.NET应用程序中调用它。
您可能需要一个表值类型来传递数据列表,如下所示:
CREATE TYPE List_Of_Items AS TABLE (
ItemID INT NOT NULL,
Quantity INT NOT NULL
)
程序可能如下所示
CREATE PROC Insert_Order_With_Details (
@CustomerId INT,
@Items List_Of_Items
) AS
BEGIN
BEGIN TRANSACTION
INSERT INTO OrdersMaster (CustomerId) VALUES @CustomerId
DECLARE @OrderID INT
SET @OrderID = SCOPE_IDENTITY() --last assigned id
INSERT INTO OrderDetails (OrderId, CustomerId, ItemId, Quantity)
SELECT @OrderID, @CustomerID, ItemID, Quantity
FROM @Items
COMMIT
END
然后在C#中,我建议创建创建TVP的方法。它并不像你想的那么简单。这需要using Microsoft.SqlServer.Server
和using Dapper.Tvp
。
//This is a shell to create any kind of TVP
private static void AddTableCore<T>(
this DynamicParametersTvp dp,
string tvpTypeName,
Func<T, SqlDataRecord> valueProjection,
IEnumerable<T> values,
string parameterTableName)
{
var tvp = values
.Select(valueProjection)
.ToList();
//If you pass a TVP with 0 rows to SQL server it will error, you must pass null instead.
if (!tvp.Any()) tvp = null;
dp.Add(new TableValueParameter(parameterTableName, tvpTypeName, tvp));
}
//This will create your specific Items TVP
public static void AddItemsTable(this DynamicParametersTvp dp, IEnumerable<Item> items, string parameterTableName = "Items")
{
var columns = new[]
{
new SqlMetaData("ItemID", SqlDbType.Int)
new SqlMetaData("Quantity", SqlDbType.Int)
};
var projection = new Func<Item, SqlDataRecord>(item =>
{
var record = new SqlDataRecord(columns);
record.SetInt32(0, item.Id);
record.SetInt32(1, item.Quantity);
return record;
});
AddTableCore(dp, "Items", projection, items, parameterTableName);
}
然后你需要查询的地方可能会这样做:
using (var cn = new SqlConnection(myConnectionString))
{
var p = new DynampicParametersTvp(new {
CustomerId = myCustomerId
});
p.AddItemsTable(items);
cn.Execute("Insert_Order_With_Details", p, commandType: CommandType.StoredProcedure);
}
commandType
参数非常重要。它默认为纯SQL文本,如果发送proc的名称,则会出错。
如果您想一次输入多个订单,则需要使用表值参数和Dapper.Tvp包。
请参阅此问题Using Dapper.TVP TableValueParameter with other parameters以及来自Microsoft https://docs.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine的TVP文档。我不认为所有SQL供应商都支持TVP。
答案 1 :(得分:1)
使用其他答案中提到的存储过程是很好的解决方案。我的回答是在没有存储过程的情况下实现相同的。
您必须使用交易。这样,将提交或回滚所有更改。以下代码假定您使用Identity
作为主键。有关我在以下代码中使用的@@IDENTITY
的讨论,请参阅this问题。
虽然代码不完整,但我已经详细说明了解释步骤。
using (var connection = new SqlCeConnection("connection_string"))
{
connection.Open();
//Begin the transaction
using (var transaction = connection.BeginTransaction())
{
//Create and fill-up master table data
var paramMaster = new DynamicParameters();
paramMaster.Add("@XXX", ...);
....
//Insert record in master table. Pass transaction parameter to Dapper.
var affectedRows = connection.Execute("insert into OrdersMaster....", paramMaster, transaction: transaction);
//Get the Id newly created for master table record.
//If this is not an Identity, use different method here
newId = Convert.ToInt64(connection.ExecuteScalar<object>("SELECT @@IDENTITY", null, transaction: transaction));
//Create and fill-up detail table data
//Use suitable loop as you want to insert multiple records.
//for(......)
foreach(OrderItem item in orderItems)
{
var paramDetails = new DynamicParameters();
paramDetails.Add("@OrderMasterId", newId);
paramDetails.Add("@YYY", ...);
....
//Insert record in detail table. Pass transaction parameter to Dapper.
var affectedRows = connection.Execute("insert into OrderDetails....", paramDetails, transaction: transaction);
}
//Commit transaction
transaction.Commit();
}
}