首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >EntityFramework6事务中的MySQL更改值

EntityFramework6事务中的MySQL更改值
EN

Stack Overflow用户
提问于 2014-10-09 09:41:56
回答 2查看 886关注 0票数 0

我希望在事务中将1添加到数据库中的值。我想确保记录是正确更新的,并且在这段时间内没有被其他人更改。

我有下面的代码,我认为这是可行的,但我仍然可以在调试期间暂停,将数据库中的记录更改为不同的内容,然后它就变得不一致了。

这是我的密码:

代码语言:javascript
复制
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();
    }
}

事态转变:

  1. 最初,数据库中的UseCount = 0
  2. 我在SaveChanges()之前运行代码
  3. 我进入数据库并将UseCount更改为5
  4. 我允许调用SaveChanges() (不应该不被阻塞)
  5. 数据库中的UseCount值将是1

通常情况下,我会使用SELECT FOR UPDATE暂时锁定记录,但我最初使用的是PHP + MySQL。

我读过这个锁是不可能的,所以我想知道这是如何实现的。

这很重要,因为我不希望人们使用密码超过设定的次数!如果有人可以在同一时间内更改值,则不能保证正确的使用次数。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-10-09 14:44:37

我创建了一个存储过程,它返回我想返回的值的SELECT语句,这是一个Success int

代码语言:javascript
复制
DROP PROCEDURE IF EXISTS UsePassword;

DELIMITER //
CREATE PROCEDURE UsePassword (QuizId INT(11), PasswordText VARCHAR(25))
BEGIN

  /* Get current state of password */
    SELECT RemainingUses, UnlimitedUses, Deleted INTO @RemainingUses, @UnlimitedUses, @Deleted FROM QuizPassword q WHERE q.QuizId = QuizId AND `Password` = PasswordText AND Deleted = 0 LIMIT 0,1 FOR UPDATE;

    IF FOUND_ROWS() = 0 OR @Deleted = 1 THEN

        /* Valid password not found for quiz */
        SET @Success = 0;   

    ELSEIF @UnlimitedUses = 1 THEN

        UPDATE QuizPassword SET UseCount = UseCount + 1 WHERE QuizId = QuizId AND `Password` = PasswordText;
        SET @Success = ROW_COUNT();

    ELSEIF @RemainingUses > 0 AND @UnlimitedUses = 0 THEN

        UPDATE QuizPassword SET UseCount = UseCount + 1, RemainingUses = RemainingUses - 1 WHERE QuizId = QuizId AND `Password` = PasswordText;
        SET @Success = ROW_COUNT();

    ELSE

        SET @Success = 0;

    END IF;

  /* Return rows changed rows */
  SELECT @Success AS Success;

END //
DELIMITER;

我不得不创建一个新的对象来保存这些值,我在里面只有一个字段,但是你可以放更多。

代码语言:javascript
复制
// Class to hold return values from stored procedure
public class UsePasswordResult
{
    public int Success { get; set; }

    // could have more fields...
}

我将最后的代码简化为这样,它调用存储过程并将值赋值给对象中的成员变量:

代码语言:javascript
复制
using (var transaction = this.Context.Database.BeginTransaction())
{
    try
    {

        if (quiz.PasswordRequiredToTakeQuiz())
        {
            // Attempt to use password
            UsePasswordResult result = this.Context.Database.SqlQuery<UsePasswordResult>("CALL UsePassword({0}, {1})", quiz.Id, model.QuizPassword).FirstOrDefault();

            // Check the result of the password use
            if (result.Success != 1)
            {
                // Failed to use the password
                ViewData.ModelState.AddModelError("QuizPassword", "Sorry the password you provided has expired or is not valid for this quiz");
            }
        }

        // Is model state still valid after password checks?
        if (ModelState.IsValid)
        {
            // Do stuff
        }

        transaction.Commit();
    }
    catch(Exception)
    {
        transaction.Rollback();
    }
    finally
    {
        transaction.Dispose();
    }
}

从存储过程返回的值必须与作为结果生成的类中的名称完全相同。

因为我在事务using语句中调用using,所以语句锁定记录,因为我选择它为SELECT... FOR UPDATE,直到在代码中调用transaction.Commit()/Rollback()/Dispose() .因此,防止任何人试图使用密码,而其他任何人。

票数 0
EN

Stack Overflow用户

发布于 2014-10-09 10:00:11

一种解决方案是使用普通sql (ado.net)和悲观锁定。

代码语言:javascript
复制
BEGIN TRANSACTION

SELECT usecount, unlimiteduses FROM quizpassword WITH (UPDLOCK, HOLDLOCK) WHERE id = x;

// check usecount here

// only do this if unlimitedUses == false
UPDATE quizpassword SET usecount = usecount + 1 WHERE id = x;

UPDATE quizpassword SET remaininguses = remaininguses -1 WHERE id = x;


COMMIT TRANSACTION // (lock is released)
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26275199

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档