gpt4 book ai didi

c# - 如何使用 Entity Framework 插入或选择

转载 作者:太空宇宙 更新时间:2023-11-03 12:47:32 26 4
gpt4 key购买 nike

我正在使用 Entity Framework v6 并且我正在尝试确保我可以执行原子插入或选择记录(如果它尚不存在)以便在服务器场(或多线程)中我可以保证我不会违反唯一键约束。

我有一个简单的示例,其中包含一个这样的表和一个具有 2 个属性的相应简单模型。

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 返回 false 后不会插入对象。

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);
}
}

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 为 null

默认事务隔离级别

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.

最佳答案

这些都不能保证有效。你缺少的那 block 是Transaction Isolation Level .

SQL Server 默认隔离级别是READ COMMITTED。这只有最少的锁定保护。这意味着另一个命令可以在评估您的 IF 条件之后但在插入新行之前插入新项目。原始 SQL 服务器命令大大不太可能遇到这种情况,因为它是在命令之间没有往返的情况下进行处理的:另一个服务器干扰的时间更短。

如果将隔离级别设置为可序列化,则可以保证插入项目或选择现有项目。在 Entity Framework 中,您的代码将如下所示:

using (var myContext = new MyContext())
{
using (var transaction = myContext.Database.BeginTransaction(System.Data.IsolationLevel.Serializable))
{
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();
}
}

关于c# - 如何使用 Entity Framework 插入或选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36732415/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com