I am working with Entity Framework database first, and I have a table that stores historical values using baseline ids. We store parent/child links on this table using the baseline id. The following columns makeup this design:-
Example data for clarity
Id BaselineId ParentBaselineId Latest
1 1 NULL 0
2 1 NULL 1
3 2 1 0
4 2 1 1
This shows two items, each with two revisions. Baseline 1 is the parent of baseline 2.
My issue is that for the reasons listed below I lookup the next available baseline in C# and manually specify the BaselineId/ParentBaselineId to be saved. When two users trigger this method at the same time, they save the same Baseline ids, as the save does not complete before the second users looks up the next available baseline id.
What measures can I take to ensure that the same baseline won't be used by two users running the method at the same time?
My C# looks something like this
using (var tx = new TransactionScope())
{
using (var context = new DbContext(connectionString))
{
int baseline = context.MyTable.Max(e => e.BaselineId);
context.MyTable.Add(new MyTable() {BaselineId = baseline + 1, Latest = true});
context.MyTable.Add(new MyTable() { BaselineId = baseline + 2, ParentBaselineId = baseline + 1, Latest = true });
context.SaveChanges();
}
tx.Complete();
}
Using @Steve Greene 's suggestion, I was able to use an SQL sequence. After creating a new sequence in my database and setting the start value to match my existing data, I updated my code to the following.
public long NextBaseline(DbContext context)
{
DataTable dt = new DataTable();
var conn = context.Database.Connection;
var connectionState = conn.State;
try
{
if (connectionState != ConnectionState.Open)
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT NEXT VALUE FOR MySequence;";
using (var reader = cmd.ExecuteReader())
{
dt.Load(reader);
}
}
}
catch (Exception ex)
{
throw new HCSSException(ex.Message, ex);
}
finally
{
if (connectionState != ConnectionState.Open)
conn.Close();
}
return Convert.ToInt64(dt.AsEnumerable().First().ItemArray[0]);
}
public void Save()
{
using (var tx = new TransactionScope())
{
using (var context = new DbContext(connectionString))
{
var parent = new MyTable() { BaselineId = NextBaseline(context), Latest = true };
var child = new MyTable() { BaselineId = NextBaseline(context), ParentBaselineId = parent.BaselineId, Latest = true }
context.MyTable.Add(parent);
context.MyTable.Add(child);
context.SaveChanges();
}
tx.Complete();
}
}
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.