【问题标题】:How to execute multiple raw SQL commands in a single transaction?如何在单个事务中执行多个原始 SQL 命令?
【发布时间】:2019-12-03 18:54:04
【问题描述】:

我需要使用.net core 应用程序将多个值插入到多个表中。

第一个表 [AI_Table] 包含键,第二个 表 [AI_TableLanguage] 包含多种语言的名称。 [AI_Table] 具有 IDENTITY 主键,因此 DB 生成它。我需要从 [AI_Table] 中获取该密钥并将其插入 [AI_TableLanguage]。

首先,我不确定 get max key 是正确的方法,其次,如果两个或多个用户同时访问此表怎么办?会不会死机,还是什么?所以我的问题是,如果不是,这个http post action结果如何作为一个单一的事务,所以没有人可以中断这些插入?

总体思路:

  • 插入 [AI_Table](DB 生成 PK)。
  • 获取该 PK 并将其插入 [AI_TableLanguage] 三次。

我阅读了一些其他得到解答的问题,还阅读了 Microsoft 文档。我找不到任何使用 RAW SQL 的答案。另外,我找不到整个 IActionResult 是否充当单个事务。

[HttpPost, ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(IzvorDropDown IzvorDropDown, InstitucijaDropDown InstitucijaDropDown, UpitTabeleCreate UpitTabeleCreate)
{
    //insert
    await context.Database.ExecuteSqlCommandAsync($"INSERT INTO AI_Table (IAICode, VOD, VDO, user_insert, user_update, date_insert, date_update) VALUES ({IzvorDropDown.IAIcode},{UpitTabeleCreate.VOD},{UpitTabeleCreate.VDO},{TrenutniKorisnik},{TrenutniKorisnik},{TrenutnoVreme},{TrenutnoVreme})");            

    //get max key
    var item = await context.NextIAITableCodeGenerator.FromSql($"SELECT ISNULL(MAX(IAITableCode), 0) AS NextIAIcode FROM AI_Table").ToListAsync();
    int IAITableCode = item[0].NextIAIcode;     

    //insert continue
    await context.Database.ExecuteSqlCommandAsync($"INSERT INTO AI_TableLanguage (IAITableCode, LanguageID, TableName) VALUES ({IAITableCode},1,{UpitTabeleCreate.TableNameCyr})");  //cirilica - radi
    await context.Database.ExecuteSqlCommandAsync($"INSERT INTO AI_TableLanguage (IAITableCode, LanguageID, TableName) VALUES ({IAITableCode},2,{UpitTabeleCreate.TableNameLat})"); //latinica
    await context.Database.ExecuteSqlCommandAsync($"INSERT INTO AI_TableLanguage (IAITableCode, LanguageID, TableName) VALUES ({IAITableCode},3,{UpitTabeleCreate.TableNameEng})"); //engleski

    return RedirectToAction(nameof(Index));
}

代码本身有效。它会正确插入数据库,但我不知道当有许多用户试图同时访问这些表时它是否会继续这样做。

【问题讨论】:

  • 这整件事不需要原始 sql,您可以使用 ef 事务来完成它,并且更清洁地获得相同的结果。或者您可以将其写入单个查询而不是多个命令和用户 Context.Database.BeginTransaction() (我不记得确切的路径)

标签: sql asp.net-core-mvc entity-framework-core


【解决方案1】:

您认为这会导致并发问题是正确的。

使用存储过程并搜索如何使用SCOPE_IDENTITY

【讨论】:

  • 我认为使用存储过程(如果可能的话---OP 可能没有能力在数据库端创建新的 SP)是最好的选择。在数据库方面,您也可以output 新 ID 而不是使用SCOPE_IDENTITY
【解决方案2】:

数据库处理提交日志。并且在每次提交中,数据库都会检查记录(或序列)的最终版本(状态)和提交日志本身提供的版本号,如果这些信息之间存在不一致,您可能会遇到像 “这样的异常自您开始后,记录已被其他用户更改”。因此,使用单次提交进行这些(在一个事务中相互关联)插入/更新/删除操作非常重要。这样您就可以使用数据库的一致性机制,并且可以确定代码在任何负载情况下的行为。

【讨论】:

    【解决方案3】:

    100% 确保获得正确 IDENTITY 的一种方法是使用 OUTPUT 子句documentation

    在此示例中,FieldID 是表 TableXYZ 的 IDENTITY KEY

    存储过程示例:

    USE [DataBaseX]
    GO
    IF OBJECT_ID('spTableXYZAdd') IS NOT NULL
        DROP PROC spTableXYZAdd
    GO
    
    CREATE PROCEDURE dbo.spTableXYZAdd
                    @FieldOne int,
                    @FieldTwo int,
                    @FieldThree DateTime,
                    @ReturnIdentityValue int OUTPUT
    
    AS
    BEGIN
    
            DECLARE @TempTableXYZ table ( TempID int )
    
            INSERT INTO TableXYZ (FieldOne,FieldTwo,FieldThree) 
            OUTPUT INSERTED.FieldID into @TempTableXYZ
            VALUES (@FieldOne,@FieldTwo,@FieldThree)
    
            SELECT @ReturnIdentityValue = (SELECT TempID FROM @TempTableXYZ)
    END
    GO
    

    您的代码将是这样的:

    public static int AddSomeRecord(RecordClass oRecordObj)
    {
        int iRetVal = 0;
        using (SqlConnection conn = Connection.GetConnection())
        {
            using (SqlCommand cmd = new SqlCommand("spTableXYZAdd", conn))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@FieldOne", oRecordObj.FieldOne);
                cmd.Parameters.AddWithValue("@FieldTwo", oRecordObj.FieldTwo);
                cmd.Parameters.AddWithValue("@FieldThree", oRecordObj.FieldThree);
                cmd.Parameters.Add("@ReturnIdentityValue", SqlDbType.Int).Direction = ParameterDirection.Output;
                conn.Open();
                cmd.ExecuteNonQuery();
                iRetVal = Convert.ToInt32(cmd.Parameters["@ReturnIdentityValue"].Value);
            }
        }
        return iRetVal;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-06-05
      • 1970-01-01
      • 2012-11-20
      • 1970-01-01
      • 2020-12-15
      • 1970-01-01
      • 2021-03-13
      • 2021-12-01
      • 2020-05-27
      相关资源
      最近更新 更多