2015-06-07 2 views
1

Предположим, у меня есть таблица account_profile, в которой есть поле Score, похожее на деньги учетной записи (тип базы данных - BIGINT(20), а тип EntityFramework - long, потому что мне не нужно десятичное число). Теперь у меня есть следующие функции:Обеспечить параллельные (денежные) транзакции в Entity Framework?

public long ChangeScoreAmount(int userID, long amount) 
{ 
    var profile = this.Entities.account_profile.First(q => q.AccountID == userID); 

    profile.Score += amount; 

    this.Entities.SaveChanges(); 

    return profile.Score; 
} 

Однако, я боюсь, что когда ChangeScoreAmount называют несколько раз одновременно, окончательная сумма не будет правильным.

Вот мои текущие решения я имею в виду:

  • Добавление замок с статической переменной блокировки в функции ChangeScoreAmount, так как сам класс может быть реализован несколько раз, когда это необходимо. Это выглядит следующим образом:

    public long ChangeScoreAmount(int userID, long amount) 
    { 
        lock (ProfileBusiness.scoreLock) 
        { 
         var profile = this.Entities.account_profile.First(q => q.AccountID == userID); 
    
         profile.Score += amount; 
    
         this.Entities.SaveChanges(); 
    
         return profile.Score; 
        } 
    } 
    

Проблема заключается в том, я никогда не пробовал блокировку static переменной, так что я не знаю, если это действительно безопасно, и если какой-нибудь тупик произойдет. Более того, может быть плохо, если где-то в другом месте вне этой функции, переход на поле Score применяется на полпути.

КИ это уже не вариант, потому что мой сервер приложения будут работать на нескольких сайтах, что означает, что переменный запирающий не могут быть использованы

  • Создания хранимой процедуры в базе данных, и называют это Хранятся процедура в функции. Тем не менее, я не знаю, существует ли «атомарный» способ создания этой процедуры хранения, поэтому его можно вызывать только один раз за раз, так как мне все еще нужно восстановить значение, изменив его, а затем обновить его снова?

Я использую MySQL Community 5.6.24 и MySQL .NET Connector 6.9.6, если это имеет значение.

ПРИМЕЧАНИЕ Мое серверное приложение может быть запущено на нескольких серверных машинах.

+0

Возможный дубликат [получение блокировки статического объекта должен блокироваться на других потоках запросов?] (Http://stackoverflow.com/questions/7681725/acquiring-a-lock-on-a-static-object-should -block-on-other-request-threads) – FrankerZ

ответ

1

Вы можете использовать транзакции sql с повторяемым уровнем изоляции чтения вместо блокировки приложения. Например, вы можете написать

public long ChangeScoreAmount(int userID, long amount) 
{ 
    using(var ts = new TransactionScope(TransactionScopeOption.RequiresNew, 
     new TransactionOptions { IsolationLevel = IsolationLevel.RepeatableRead }) 
    { 
     var profile = this.Entities.account_profile.First(q => q.AccountID == userID); 

     profile.Score += amount; 

     this.Entities.SaveChanges(); 

     ts.Complete(); 

     return profile.Score; 
    }  
} 

Transaction что АккаунтПрофиль ГАРАНТИИ запись не будет изменено в БД, пока вы не совершали или откат.

+0

Используется ли, когда у моего сервера несколько экземпляров приложения или когда мое приложение запускается на нескольких серверах? Я считаю, что код является серверной стороной за пределами базы данных? –

+1

Это неважно, сколько экземпляров приложений или серверных машин у вас есть. Транзакция в режиме реального времени в базе данных и в базе данных предотвращает любые изменения (от других транзакций) до записи, которая была прочитана в вашей транзакции с повторяемым уровнем изоляции чтения. – Oleg

+0

Могу ли я задать еще один вопрос: «TransactionScope» не имеет ни параметра «Connection», ни «Entities» в параметрах, как он знает, что «изолировать» и «жить в базе данных и движке базы данных»? Что «если» у меня одновременно работают два подключения к базе данных? –

Смежные вопросы