エンティティフレームワークv6を使用しています。アトミックInsertまたはSelectを実行してレコードが存在しない場合は、サーバーのファームまたは複数のスレッド)私は一意のキー制約違反がないことを保証することができます。エンティティフレームワークでInsertOrSelectを挿入する方法
私はこのような表と2つのプロパティを持つ対応する単純なモデルを持つ簡単な例を持っています。私はEntity Frameworkのコードを記述する場合
CREATE TABLE [dbo].[NewItem]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](40) NOT NULL,
CONSTRAINT [PK_NewItem] PRIMARY KEY CLUSTERED ([ID] ASC),
CONSTRAINT [IX_NewItem] UNIQUE NONCLUSTERED ([Name] ASC)
)
することは、私は、オブジェクトが偽.Any
戻った後に挿入されないことを保証することはできません。
using (var myContext = new MyContext())
{
using (var transaction = myContext.Database.BeginTransaction())
{
if (!myContext.NewItems.Any(item => item.Name == identifier))
{
var newItem = new NewItem { Name = identifier };
myContext.NewItems.Add(newItem);
try
{
var result = myContext.SaveChanges();
}
catch (Exception ex)
{
Debug.Print(ex.ToString());
throw;
}
}
transaction.Commit();
}
}
私はSqlCommand
オブジェクトを介して直接SQL文を使用して、この同じコードを実行するためだった場合、これは私が私の知る限り、それは常に動作します言うことができるように使用してしまうものです。
using (var connection = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
{
using (var cmd = connection.CreateCommand())
{
cmd.CommandText = "IF (NOT EXISTS(SELECT ID FROM NewItem WHERE Name = @name)) " +
" BEGIN " +
" INSERT INTO NewItem VALUES (@name) " +
" SELECT SCOPE_IDENTITY() AS ID " +
" END " +
" ELSE " +
" BEGIN " +
" SELECT ID FROM NewItem WHERE Name = @name " +
" END";
var nameParam = new SqlParameter("@name", System.Data.SqlDbType.VarChar, 40);
nameParam.Value = Name;
cmd.Parameters.Add(nameParam);
connection.Open();
var result = cmd.ExecuteScalar();
connection.Close();
Id = Convert.ToInt32(result);
}
}
が既に存在する場合、レコードは、私はちょうどあるものを得る、それが挿入されます存在していない場合、私は私の生SqlCommand
ように同じ動作を実行できることをEntity Frameworkを持ついくつかの方法があります原子的な操作の中にありますか?
これを実証するには、複数のサーバーをシミュレートするために使用したスレッドコードがあります。
private static ExecutionMode mode;
private static ManualResetEventSlim wait;
private static string identifier;
private enum ExecutionMode
{
None = 0,
Entityframework,
RawSql
}
static void Main(string[] args)
{
//Comment out the relevant item to test
//mode = ExecutionMode.RawSql;
mode = ExecutionMode.Entityframework;
wait = new ManualResetEventSlim(false);
identifier = Guid.NewGuid().ToString();
var threads = new List<Thread>();
for (var i = 0; i < 20; i++)
{
var t = new Thread(RunCreate);
threads.Add(t);
t.Start();
}
wait.Set();
for (var i = 0; i < 20; i++)
{
var t = threads[i];
t.Join();
}
}
private static void RunCreate()
{
wait.Wait();
switch (mode)
{
case ExecutionMode.Entityframework:
{
//perform the Entityframework code above
}
break;
case ExecutionMode.RawSql:
{
var i = new NewItem { Name = identifier };
i.InsertOrSelect(); //This is just using the code for the raw SQL Statements
}
break;
}
}
例外をループex.Messageを表示し、Entityframeworkの経験exception->のInnerExceptionのInnerExceptionはSerializable tranaction分離してデフォルトのトランザクション分離レベル
BeginTransaction()
A first chance exception of type 'System.Data.Entity.Infrastructure.DbUpdateException'
occurred in EntityFramework.dll
An error occurred while updating the entries.
See the inner exception for details.
An error occurred while updating the entries.
See the inner exception for details.
Violation of UNIQUE KEY constraint 'IX_NewItem'. Cannot insert duplicate key in object 'dbo.NewItem'.
The duplicate key value is (f32c6a59-1462-49c3-85e2-5126c96ad484).
で
ヌルになるまで
BeginTransaction(System.Data.IsolationLevel.Serializable)
A first chance exception of type 'System.Data.Entity.Infrastructure.DbUpdateException'
occurred in EntityFramework.dll
Transaction (Process ID 59) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
私だけでBeginTransactionに 'System.Data.IsolationLevel.Serializable'を追加しようとしたと私は違反と同じ結果を得ます。 –
この違反は、コードが1つのサーバー上で1つのスレッドでのみ実行されている場合に発生しますか?あなたが使用している正確なコードを投稿したコードはありますか?私はそれが必要な詳細だけを表示するように変更されていると仮定します。 –
違反は単一のスレッド/サーバーシナリオで発生しません* –