我有以下型号:
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语句会执行,但transactionLog不会添加到数据库中。如果没有事务提交,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
编辑:
好的,更多信息: 似乎
currentAccount.CardCash -= money;
实际上并没有修改实际记录。检查数据库中的值时,它是旧的。但是,即使我尝试使用
再次从数据库中获取对象,控制台上打印的值也会减少account = dbContext.CardAccounts.Where(x => x.CardNumber == account.CardNumber).FirstOrDefault();
在第二张卡片信息打印之前。
编辑2:
偏离来自上下文本身。修改上下文中的值,而不修改数据库中的值。如何避免上下文的无效状态(除了删除事务..)?上下文的重新创造是唯一的解决方案吗?
答案 0 :(得分:0)
我认为您不需要为您的方案创建交易。我认为这样做更容易:
using (var dbContext = new ATMDbContext())
{
var currentAccount = dbContext.CardAccounts.FirstOrDefault(x => x.CardNumber == cardNumber);
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);
}
}
当您调用SaveChanges
方法时,EF会创建一个事务来保存您之前执行的所有操作。
此外,如果内部交易失败,您也不必担心。 DbContextTransaction.Dispose
块的末尾将调用using
方法,如果事务未成功提交,它将自动回滚事务。
当您需要多次调用SaveChanges时使用事务:
using(var scope = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
// Do something
context.SaveChanges();
// Do something else
context.SaveChanges();
scope.Complete();
}