实体框架中的一半事务执行
本文关键字:事务 执行 一半 框架 实体 | 更新日期: 2023-09-27 18:36:43
我有以下型号:
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();
}
}
}
当我取消注释"交易。提交()' 一切正常。但是,当它被注释时,更新语句将执行,但事务日志不会添加到数据库中。如何在不提交事务的情况下提交更新语句?保存更改是否过早提交事务?
这是我正在使用的代码:
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:
偏差来自上下文本身。上下文中的值被修改,而数据库中的值不会被修改。如何规避上下文的无效状态(删除事务除外..)?上下文的重新创建是唯一的解决方案吗?
我认为您不需要为您的场景创建事务。我认为这样做更容易:
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();
}