Я использую Entity Framework v6, и я пытаюсь убедиться, что могу выполнять атомарную Вставку или Выбрать запись, если она еще не существует, чтобы в ферме серверов (или несколько потоков) Я могу гарантировать, что я не получаю уникальное нарушение ограничения ключа.Как вставитьOrSelect с платформой Entity
У меня есть простой пример с таблицей, подобной этой, и соответствующей простой моделью с двумя свойствами.
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)
)
Когда я пишу код Entity Framework, я не могу гарантировать, что объект не вставляются после .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();
}
}
Если бы я должен был выполнить такой же код, используя прямое заявление SQL через SqlCommand
объект, это то, что я хотел бы использовать, и, насколько я могу сказать, что это всегда работает.
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);
}
}
Есть ли какой-то способ с Entity Framework, которые я могу выполнять ту же операцию, как мой сырой SqlCommand
так, что если запись не существует, то он получает вставлено и если он уже существует, я просто получить тот, который там в атомной операции?
Чтобы продемонстрировать это некоторые из кода потоков, которые я использовал для моделирования нескольких серверов.
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;
}
}
Исключения опыт работы с Entityframework, показывая ex.Message зацикливание через exception-> InnerException до InnerException не является нулевым
С По умолчанию Уровень изоляции транзакций
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).
С Сериализуемый tranaction Изоляция
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.
Я просто попытался добавить 'System.Data.IsolationLevel.Serializable' к BeginTransaction и я получаю тот же результат с нарушением. –
Имеет ли это нарушение, когда код работает только на одном сервере, в одном потоке? Является ли код, в котором вы разместили точный код, который вы используете? Я предполагаю, что он модифицирован, чтобы отображать только необходимые данные. –
Нарушение * не * происходит в одном сценарии потока/сервера –