У меня есть следующие модели:Половина выполнения транзакции в Entity Framework
public class CardAccount
{
public int ID { get; set; }
[Index(IsUnique = true)]
[Required]
[StringLength(10)]
public string CardNumber { get; set; }
[Required]
[StringLength(4)]
public string CardPIN { get; set; }
[Required]
public decimal CardCash { get; set; }
}
-
[Table("TransactionHistory")]
public class TransactionHistory
{
public int ID { get; set; }
[Index(IsUnique = false)]
[Required]
[StringLength(10)]
public string CardNumber { get; set; }
[Required]
public DateTime TransactionDate { get; set; }
[Required]
public decimal Ammount { get; set; }
}
Контекст базы данных:
public class ATMDbContext : DbContext
{
public ATMDbContext()
: base("ATM")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<ATMDbContext, Configuration>());
}
public IDbSet<CardAccount> CardAccounts { get; set; }
public IDbSet<TransactionHistory> TransactionHistory { get; set; }
}
и класс ATM с методом выйти:
public class ATMClient
{
private const int CardNumberLength = 10;
private const int CardPINLength = 4;
private ATMDbContext dbContext;
private IOutputProvider outputProvider;
public ATMClient(ATMDbContext dbContext)
: this(dbContext, new ConsoleOutputProvider())
{
}
public ATMClient(ATMDbContext dbContext, IOutputProvider outputProvider)
{
this.dbContext = dbContext;
this.outputProvider = outputProvider;
}
public void WithdrawMoney(string cardNumber, string cardPIN, decimal money)
{
if (string.IsNullOrWhiteSpace(cardNumber))
{
throw new ArgumentNullException("The card number is null.");
}
if (cardNumber.Length != CardNumberLength)
{
throw new ArgumentException(String.Format("The card number is invalid. Please, state a proper {0} digit card number.", CardNumberLength));
}
if (string.IsNullOrWhiteSpace(cardPIN))
{
throw new ArgumentNullException("The card pin is null.");
}
if (cardPIN.Length != CardPINLength)
{
throw new ArgumentException(String.Format("The card pin is invalid. Please, state a proper {0} digit card pin.", CardPINLength));
}
if (money < 0)
{
throw new ArgumentException("Invalid amount to withdraw. Please, state a valid positive number.");
}
using (var transaction = dbContext.Database.BeginTransaction(IsolationLevel.RepeatableRead))
{
var currentAccount = dbContext.CardAccounts.Where(x => x.CardNumber == cardNumber).FirstOrDefault();
try
{
if (currentAccount == null)
{
throw new ArgumentException("Invalid card number.");
}
if (currentAccount.CardPIN != cardPIN)
{
throw new ArgumentException("Invalid pin number.");
}
if (currentAccount.CardCash < money)
{
throw new InvalidOperationException("Insufficient funds.");
}
currentAccount.CardCash -= money;
var transactionLog = new TransactionHistory();
transactionLog.CardNumber = cardNumber;
transactionLog.TransactionDate = DateTime.Now;
transactionLog.Ammount = money;
dbContext.TransactionHistory.Add(transactionLog);
dbContext.SaveChanges();
}
catch (Exception ex)
{
outputProvider.PrintLine(ex.Message);
transaction.Rollback();
}
//transaction.Commit();
}
}
}
Когда я раскомментирую 'transaction.Commit()' все работает нормально. Однако при комментировании выполняется инструкция update, но транзакционный журнал не добавляется в базу данных. Как выполняется инструкция обновления без транзакции? Сохраняет ли SaveChanges преждевременную транзакцию?
Вот код, я использую:
public static void Main()
{
var dbContext = new ATMDbContext();
var atm = new ATMClient(dbContext);
var account = dbContext.CardAccounts.Where(x => x.CardCash >= 10000).FirstOrDefault();
Console.WriteLine(account.CardNumber);
Console.WriteLine(account.CardCash);
atm.WithdrawMoney(account.CardNumber, account.CardPIN, 10000m);
Console.WriteLine(account.CardNumber);
Console.WriteLine(account.CardCash);
var transactionHistory = dbContext.TransactionHistory.Where(x => x.CardNumber == account.CardNumber).FirstOrDefault();
Console.WriteLine(transactionHistory.CardNumber);
}
Это приводит к NullReferenceException как уже было сказано:
var transactionHistory = dbContext.TransactionHistory.Where(x => x.CardNumber == account.CardNumber).FirstOrDefault();
Console.WriteLine(transactionHistory.CardNumber);
Выход:
2504478325
21835.56
2504478325
11835.56
EDIT:
Хорошо, немного больше информации: кажется
currentAccount.CardCash -= money;
фактически не изменять фактическую запись. Когда вы проверяете значение в базе данных, оно является старым. Тем не менее, значение печатается на консоли декрементируются, даже если я пытаюсь получить объект из базы данных снова с помощью
account = dbContext.CardAccounts.Where(x => x.CardNumber == account.CardNumber).FirstOrDefault();
до второй информации карты печати.
EDIT 2:
Отклонение исходит от самого контекста. Значение в контексте изменяется, а значение в базе данных - нет. Как избежать недопустимого состояния контекста (кроме удаления транзакции ..)? Является ли рекреация контекста единственным решением?
Почему вы используете транзакции в методе WithdrawMoney? – octavioccl
Ну, за одну остановку проглотить ошибки. Эта практика может привести к тому, что приложение хромает в странном состоянии и вызывает всевозможные странные записи. Я передумаю этот вопрос, когда вы это исправите. – usr
@usr Извините, я не понял. – zhulien