I need to insert several values into several tables using .net core
app.
First table [AI_Table] contains keys and second table [AI_TableLanguage] contains names in mutiple languages. [AI_Table] has IDENTITY primary key, so DB generates it. I need to get that key from [AI_Table] and insert it into [AI_TableLanguage].
Firstly, I am not sure get max key is the right approach, and secondly, what if two or more users approach this table at the same time? Will it deadlock, or what? So my question is does or if not, how can this http post action result act as one single transaction, so noone can interrupt these inserts?
General idea:
I read few other questions that got answered and also read the Microsoft documentation. I couldn't find any answer that uses RAW SQL. Also, I couldn't find if the whole IActionResult acts as single transaction.
[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));
}
The code itself works. It inserts correctly into DB, but I don't know if it will continue to do so when there are many users trying to access these tables at the same time.
You are right to assume that this will lead to concurrency issues.
Use a stored procedure and do a search for how to use SCOPE_IDENTITY
Databases work on commit logs. And in every commit, database checks the final version(state) of the record(or sequence) and the version number provided by the commit log itself and if there is a inconsistency between these informations you may take a exception like "This record has been changed by another user since you started" . So it is important to make these(related with each other in one transaction) insert/update/delete operations with a single commit . So that you can use database's consistency mechanisms and can be sure about the behaviour of the code in any load circumstance.
One way to be 100% sure you get the correct IDENTITY is to use the OUTPUT clause documentation
In this example FieldID
is the IDENTITY KEY to the table TableXYZ
Stored Prodcedure example:
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
Your code will be something like this:
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;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.