C# 在sql查询期间获取Id并用于另一个Insert语句
所以我一直在创建一个库,它使用dapper并允许用户操作数据库 我需要一些帮助来找到实现以下目标的最佳方法 假设我有一个“订单”表,一个“交易”表和一个“订单行”表。 我希望在插入时获取表“order”的增量Id,并使用它将其存储在“transaction”和“order_line”表中的一列中,我希望所有这些都在SQL事务中完成,以便在出现任何问题时可以回滚 现在,由于我的库对任何类型和操作都是动态的,所以我不确定如何处理类似的事情 以下是有关如何插入的代码: 我有两个全局变量C# 在sql查询期间获取Id并用于另一个Insert语句,c#,.net,sql-server,transactions,dapper,C#,.net,Sql Server,Transactions,Dapper,所以我一直在创建一个库,它使用dapper并允许用户操作数据库 我需要一些帮助来找到实现以下目标的最佳方法 假设我有一个“订单”表,一个“交易”表和一个“订单行”表。 我希望在插入时获取表“order”的增量Id,并使用它将其存储在“transaction”和“order_line”表中的一列中,我希望所有这些都在SQL事务中完成,以便在出现任何问题时可以回滚 现在,由于我的库对任何类型和操作都是动态的,所以我不确定如何处理类似的事情 以下是有关如何插入的代码: 我有两个全局变量 priva
private string connectionString { get; set; }
public void newConnection(string connection)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
connectionString = connection;
}
}
private List<KeyValuePair<string, object>> transactions = new List<KeyValuePair<string, object>>();
私有字符串连接字符串{get;set;}
公共连接(字符串连接)
{
if(string.IsNullOrWhiteSpace(connectionString))
{
connectionString=连接;
}
}
私有列表事务=新列表();
下面是如何调用以将类保存到数据库中:
public void Add(object item)
{
string propertyNames = "";
string propertyParamaters = "";
Type itemType = item.GetType();
System.Reflection.PropertyInfo[] properties = itemType.GetProperties();
for (int I = 0; I < properties.Count(); I++)
{
if (properties[I].Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) || properties[I].Name.Equals("AutoId", StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
if (I == properties.Count() - 1)
{
propertyNames += "[" + properties[I].Name + "]";
propertyParamaters += "@" + properties[I].Name;
}
else
{
propertyNames += "[" + properties[I].Name + "],";
propertyParamaters += "@" + properties[I].Name + ",";
}
}
string itemName = itemType.Name;
KeyValuePair<string, object> command = new KeyValuePair<string, object>($"Insert Into[{ itemName}] ({ propertyNames}) Values({ propertyParamaters})", item);
transactions.Add(command);
}
公共作废添加(对象项)
{
字符串propertyNames=“”;
字符串PropertyParameters=“”;
Type itemType=item.GetType();
System.Reflection.PropertyInfo[]properties=itemType.GetProperties();
对于(int I=0;I
有更多的方法,如编辑、删除、编辑列表、删除列表等,但在这种情况下不相关
当您要将更改提交到您调用的数据库时:
public void SaveChanges()
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction())
{
try
{
foreach (KeyValuePair<string, object> command in transactions)
{
sqlConnection.Execute(command.Key, command.Value, sqlTransaction);
}
sqlTransaction.Commit();
}
catch
{
sqlTransaction.Rollback();
throw;
}
finally
{
sqlConnection.Close();
transactions.Clear();
}
}
sqlConnection.Close();
}
transactions.Clear();
}
public void SaveChanges()
{
使用(SqlConnection SqlConnection=newsqlconnection(connectionString))
{
sqlConnection.Open();
使用(SqlTransaction SqlTransaction=sqlConnection.BeginTransaction())
{
尝试
{
foreach(事务中的KeyValuePair命令)
{
sqlConnection.Execute(command.Key、command.Value、sqlTransaction);
}
提交();
}
抓住
{
sqlTransaction.Rollback();
投掷;
}
最后
{
sqlConnection.Close();
transactions.Clear();
}
}
sqlConnection.Close();
}
transactions.Clear();
}
您可以在github.com找到我的图书馆它没有说明您正在使用哪个数据库。如果是MSSQL,您可以这样做
var id = connection.Query<int?>("SELECT @@IDENTITY").SingleOrDefault();
var id=connection.Query(“选择@@IDENTITY”).SingleOrDefault();
在执行Insert之后。这将为您提供最后一次插入的id。可以完成吗。。。对我们应该试着自己去做吗。。。我不会:)但我们无论如何都要试试 可以使此代码更简单的一些想法:
- 定义助手接口并强制数据类实现它们,或使用属性声明指定id字段和外键引用
- 研究注入或代码生成技术,以便可以在编译时而不是运行时执行某些“动态”编码和查找
注意:因为生成的语句正在使用参数,并且我们还没有读取参数值,所以在找到要插入到其他对象的Id值之后,我们将不需要重新生成保存的SQL语句。。。呸 您需要发展一些相当高级的逻辑来处理所有可能的链接组合 一些orm通过将值设置为负数,并减少每种类型的数字来实现这一点。一种新类型被添加到命令co中
public void Add(object item)
{
List<string> propertyNames = new List<string>();
Type itemType = item.GetType();
System.Reflection.PropertyInfo[] properties = itemType.GetProperties();
for (int I = 0; I < properties.Count(); I++)
{
if (properties[I].Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) || properties[I].Name.Equals("AutoId", StringComparison.CurrentCultureIgnoreCase))
{
continue;
}
propertyNames.Add(properties[I].Name);
}
string itemName = itemType.Name;
KeyValuePair<string, object> command = new KeyValuePair<string, object>
($"Insert Into[{itemName}] ({String.Join(",", propertyNames.Select(p => $"[{p}]"))}) Values({String.Join(",", propertyNames.Select(p => $"@{p}"))}); SET @OutId = SCOPE_IDENTITY();", item);
transactions.Add(command);
// Simply append your statement with a set command on an @id parameter we will add in SaveChanges()
}
public void SaveChanges()
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTransaction = sqlConnection.BeginTransaction())
{
try
{
for (int i = 0; i < transactions.Count; i++)
{
KeyValuePair<string, object> command = transactions[i];
// 1. Execute the command, but use an output parameter to capture the generated id
var cmd = sqlConnection.CreateCommand();
cmd.Transaction = sqlTransaction;
cmd.CommandText = command.Key;
SqlParameter p = new SqlParameter()
{
ParameterName = "@OutId",
Size = 4,
Direction = ParameterDirection.Output
};
cmd.Parameters.Add(p);
cmd.ExecuteNonQuery();
// Check if the value was set, non insert operations wil not set this parameter
// Could optimise by not preparing for the parameter at all if this is not an
// insert operation.
if (p.Value != DBNull.Value)
{
int idOut = (int)p.Value;
// 2. Stuff the value of Id back into the Id field.
string foreignKeyName = null;
SetIdValue(command.Value, idOut, out foreignKeyName);
// 3. Update foreign keys, but only in commands that we haven't execcuted yet
UpdateForeignKeys(foreignKeyName, idOut, transactions.Skip(i + 1));
}
}
sqlTransaction.Commit();
}
catch
{
sqlTransaction.Rollback();
throw;
}
finally
{
sqlConnection.Close();
transactions.Clear();
}
}
sqlConnection.Close();
}
transactions.Clear();
}
/// <summary>
/// Update the Id field of the specified object with the provided value
/// </summary>
/// <param name="item">Object that we want to set the Id for</param>
/// <param name="idValue">Value of the Id that we want to push into the item</param>
/// <param name="foreignKeyName">Name of the expected foreign key fields</param>
private void SetIdValue(object item, int idValue, out string foreignKeyName)
{
// NOTE: There are better ways of doing this, including using interfaces to define the key field expectations.
// This logic is consistant with existing code so that you are familiar with the concepts
Type itemType = item.GetType();
foreignKeyName = null;
System.Reflection.PropertyInfo[] properties = itemType.GetProperties();
for (int I = 0; I < properties.Count(); I++)
{
if (properties[I].Name.Equals("Id", StringComparison.CurrentCultureIgnoreCase) || properties[I].Name.Equals("AutoId", StringComparison.CurrentCultureIgnoreCase))
{
properties[I].SetValue(item, idValue);
foreignKeyName = $"{item.GetType().Name}_{properties[I].Name}";
break;
}
}
}
private void UpdateForeignKeys(string foreignKeyName, int idValue, IEnumerable<KeyValuePair<string, object>> commands)
{
foreach(var command in commands)
{
Type itemType = command.Value.GetType();
var keyProp = itemType.GetProperty(foreignKeyName);
if(keyProp != null)
{
keyProp.SetValue(command.Value, idValue);
}
}
}
public class Manifest
{
...
Driver_UserId { get; set; }
Sender_UserId { get; set; }
Receiver_UserId { get; set; }
...
}