【发布时间】:2014-10-09 09:41:56
【问题描述】:
我想在事务中将1 添加到我的数据库中的值。我想确保记录正确更新并且在此期间没有被其他人更改。
我有以下我认为可以工作的代码,但我仍然可以在调试期间暂停,将数据库中的记录更改为不同的内容,然后它变得不一致。
这是我的代码:
using (var transaction = this.Context.Database.BeginTransaction())
{
try
{
if (quiz.PasswordRequiredToTakeQuiz())
{
// Check password exists for quiz
bool passwordIsValid = quiz.QuizPasswords.Any(x => x.Password.ToLower() == model.QuizPassword.ToLower() && !x.Deleted);
QuizPassword quizPassword = quiz.QuizPasswords.Where(x => x.Password.ToLower() == model.QuizPassword.ToLower() && !x.Deleted).First();
string passwordError = "Sorry the password you provided has expired or is not valid for this quiz";
if (!passwordIsValid)
{
ViewData.ModelState.AddModelError("QuizPassword", passwordError);
}
else
{
// Password is valid for use with this quiz, but can it be used?
if (quizPassword.RemainingUses < 1 && quizPassword.UnlimitedUses != true)
{
// Password cannot be used
ViewData.ModelState.AddModelError("QuizPassword", passwordError);
}
else
{
// Password CAN be used
if (!quizPassword.UnlimitedUses)
{
quizPassword.RemainingUses--;
}
// Increase use count
quizPassword.UseCount++;
this.Context.EntitySet<QuizPassword>().Attach(quizPassword);
this.Context.Entry(quizPassword).State = EntityState.Modified;
// I can change the record UseCount value in the database at this point
// then when it saves, it becomes inconsistent with other's use of
// the password
this.Context.SaveChanges();
}
}
}
// Commit the changes
transaction.Commit();
}
catch(Exception)
{
transaction.Rollback();
}
finally
{
transaction.Dispose();
}
}
事情的转折:
- 最初,数据库中的 UseCount =
0 - 我将代码运行到
SaveChanges()之前 - 我进入数据库并将 UseCount 更改为
5 - 我允许调用 SaveChanges()(不应该被阻止)
- 数据库中的UseCount 值为
1。
通常我会使用SELECT FOR UPDATE 来临时锁定记录,但我最初使用的是 PHP + MySQL。
我读到锁是不可能的,所以我想知道如何实现。
这很重要,因为我不希望人们使用密码的次数超过设定的次数!如果有人可以同时更改该值,则不能保证正确的使用次数。
【问题讨论】:
-
简单的
UPDATE SET SomeField=SomeField+1有什么问题?SELECT FOR UPDATE或试图持有锁是一种气味,表明您以错误的方式使用数据库。过去 20 年使用数据库的可扩展方式(即超过 10 个用户)是乐观并发,它检查一行的 ROW_VERSION 以检测是否有人修改了它。 -
原因是我想利用实体框架而不是使用原始查询......也许原始查询是实现这种行为的唯一方法。
-
一开始为什么要使用 ORM?您在这里没有 OO 行为,只有您操作的数据。此外,问题在于使用了错误的并发模型(悲观与乐观)。悲观并发(即事务)会导致阻塞,应该避免。所有数据访问方法都可以正常工作,但悲观并发严重影响性能。最后,与单个更新语句相比,让任何 ORM 发出
Field+1语句太麻烦了 -
我选择使用 ORM 是因为它在我的整个应用程序中使用它从数据库中获取对象。这段代码并不是我的解决方案中唯一的一段代码,哈哈。我想我必须为此创建一个存储过程,因为我需要检查不同的变量并根据情况更改它们。如您所见,此代码取决于处于特定状态的两个值
-
重点是——你不是在这里处理对象。 ORM 涵盖一种场景(将单行映射到类实例以供离线使用),但不适用于许多其他场景,例如批处理、报告、分析或简单的值操作
标签: c# mysql transactions entity-framework-6 acid