Ich verwende Dapper ( https://github.com/StackExchange/Dapper ) im asp.net Kern myweb api Projekt. Ich habe die Anforderung, einen Stammsatz zu erstellen und eine Reihe von Zeilen in eine untergeordnete Tabelle einzufügen.
Ex. OrdersMaster table
create an order id in this table
OrderDetails table
ordermasterid
order items
Wie kann ich das mit Dapper machen? Bitte teilen Sie einige Code-Snippets, wenn möglich.
Die Verwendung der gespeicherten Prozedur, wie in der anderen Antwort erwähnt, ist eine gute Lösung. Meine Antwort implementiert das gleiche ohne Stored Procedure.
Sie müssen die Transaktion verwenden. Auf diese Weise werden alle Änderungen festgeschrieben oder zurückgesetzt. Der folgende Code geht davon aus, dass Sie Identity
als Primärschlüssel verwenden. Bitte beziehe diese Frage zur Diskussion über @@IDENTITY
, die ich im @@IDENTITY
Code verwendet habe.
Obwohl der Code nicht vollständig ist, habe ich detaillierte Kommentare zur Erklärung der Schritte gegeben.
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();
}
}
Ich würde die Einfügeoperationen als gespeicherte Transaktionsprozedur implementieren und diese dann von Ihrer .NET-Anwendung aufrufen.
Möglicherweise benötigen Sie einen Tabellenwert, um eine Datenliste wie folgt zu übergeben:
CREATE TYPE List_Of_Items AS TABLE (
ItemID INT NOT NULL,
Quantity INT NOT NULL
)
Das Verfahren könnte so aussehen
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
Dann würde ich in C # vorschlagen, Methoden zum Erstellen Ihres TVP zu erstellen. Es ist nicht so einfach, wie Sie möchten. Dies erfordert die using Microsoft.SqlServer.Server
und die 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);
}
und dann, wo Sie abfragen müssen, tun Sie vielleicht:
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);
}
Das Argument commandType
ist sehr wichtig. Es wird standardmäßig auf reinen SQL-Text gesetzt und es tritt ein Fehler auf, wenn Sie den Namen eines Procs senden.
Wenn Sie mehrere Bestellungen gleichzeitig aufgeben möchten, müssen Sie Tabellenparameter und das Dapper.Tvp-Paket verwenden.
Sehen Sie sich diese SO-Frage an Verwenden Sie Dapper.TVP TableValueParameter mit anderen Parametern sowie diese Dokumentation auf TVPs von Microsoft https://docs.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters -Datenbank-Engine Ich glaube nicht, dass alle SQL-Anbieter TVPs unterstützen.