gpt4 book ai didi

c# - 为什么数据库数据没有被更新但对象却没有错误?

转载 作者:太空狗 更新时间:2023-10-29 21:26:07 24 4
gpt4 key购买 nike

我有这个银行 ATM 模型应用程序,它实现了一些领域驱动设计架构和工作单元模式。

这个应用程序有 3 个基本功能:

  • 查看余额
  • 存款
  • 退出

这些是项目层:

ATM.Model(领域模型实体层)

namespace ATM.Model
{
public class BankAccount
{
public int Id { get; set; }
public string AccountName { get; set; }
public decimal Balance { get; set; }

public decimal CheckBalance()
{
return Balance;
}

public void Deposit(int amount)
{
// Domain logic
Balance += amount;
}

public void Withdraw(int amount)
{
// Domain logic
//if(amount > Balance)
//{
// throw new Exception("Withdraw amount exceed account balance.");
//}

Balance -= amount;
}
}
}

namespace ATM.Model
{
public class Transaction
{
public int Id { get; set; }
public int BankAccountId { get; set; }
public DateTime TransactionDateTime { get; set; }
public TransactionType TransactionType { get; set; }
public decimal Amount { get; set; }
}

public enum TransactionType
{
Deposit, Withdraw
}
}

ATM.Persistence(持久层)

namespace ATM.Persistence.Context
{
public class AppDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"[connstring]");
}

public DbSet<BankAccount> BankAccounts { get; set; }
public DbSet<Transaction> Transactions { get; set; }
}
}

namespace ATM.Persistence.Repository
{
public class RepositoryBankAccount
{
public AppDbContext context { get; }

public RepositoryBankAccount()
{
context = new AppDbContext();
}

public BankAccount FindById(int bankAccountId)
{
return context.BankAccounts.Find(bankAccountId);
}

public void AddBankAccount(BankAccount account)
{
context.BankAccounts.Add(account);
}

public void UpdateBankAccount(BankAccount account)
{
context.Entry(account).State = EntityState.Modified;
}
}
}

namespace ATM.Persistence.Repository
{
public class RepositoryTransaction
{
private readonly AppDbContext context;

public RepositoryTransaction()
{
context = new AppDbContext();
}

public void AddTransaction(Transaction transaction)
{
context.Transactions.Add(transaction);
}
}
}

namespace ATM.Persistence.UnitOfWork
{
public class UnitOfWork : IUnitOfWork
{
private readonly AppDbContext db;
public UnitOfWork()
{
db = new AppDbContext();
}

private RepositoryBankAccount _BankAccounts;
public RepositoryBankAccount BankAccounts
{
get
{
if (_BankAccounts == null)
{
_BankAccounts = new RepositoryBankAccount();
}
return _BankAccounts;
}
}

private RepositoryTransaction _Transactions;
public RepositoryTransaction Transactions
{
get
{
if (_Transactions == null)
{
_Transactions = new RepositoryTransaction();
}
return _Transactions;
}
}

public void Dispose()
{
db.Dispose();
}

public int Commit()
{
return db.SaveChanges();
}

public void Rollback()
{
db
.ChangeTracker
.Entries()
.ToList()
.ForEach(x => x.Reload());
}
}
}

ATM.ApplicationService(应用层)

namespace ATM.ApplicationService
{
public class AccountService
{
private readonly UnitOfWork uow;

public AccountService()
{
uow = new UnitOfWork();
}

public void DepositAmount(BankAccount bankAccount, int amount)
{
bankAccount.Deposit(amount);
uow.BankAccounts.UpdateBankAccount(bankAccount);

var transaction = new Transaction()
{
BankAccountId = bankAccount.Id,
Amount = amount,
TransactionDateTime = DateTime.Now,
TransactionType = TransactionType.Deposit
};

uow.Transactions.AddTransaction(transaction);

try
{
uow.Commit();
}
catch
{
uow.Rollback();
}
finally
{
uow.Dispose();
}
}

public void WithdrawAmount(BankAccount bankAccount, int amount)
{
bankAccount.Withdraw(amount);
uow.BankAccounts.UpdateBankAccount(bankAccount);
//repoBankAccount.UpdateBankAccount(bankAccount);

var transaction = new Transaction()
{
BankAccountId = bankAccount.Id,
Amount = amount,
TransactionDateTime = DateTime.Now,
TransactionType = TransactionType.Withdraw
};

uow.Transactions.AddTransaction(transaction);

try
{
uow.Commit();
}
catch
{
uow.Rollback();
}
finally
{
uow.Dispose();
}
}

public decimal CheckBalanceAmount(int bankAccountId)
{
BankAccount bankAccount = uow.BankAccounts.FindById(bankAccountId);

return bankAccount.CheckBalance();
}
}
}

ATM.ConsoleUICore

namespace ATM.ConsoleUICore
{
class Program
{
static void Main()
{
AccountService accountService = new AccountService();
RepositoryBankAccount repoBankAccount = new RepositoryBankAccount();

var bankAccount = repoBankAccount.FindById(2);

Console.WriteLine("1. Check balance");
Console.WriteLine("2. Deposit");
Console.WriteLine("3. Withdraw");
Console.WriteLine("Enter option: ");
string opt = Console.ReadLine();
switch (opt)
{
case "1":
Console.WriteLine($"Your balance is ${bankAccount.CheckBalance()}");
break;
case "2":
// User to input amount.
// Data validation to make sure amount is greater than zero.
// Pass the input amount to Application layer.

accountService.DepositAmount(bankAccount, 50);

// After getting the operation status from Application service layer.
// Print operation status here: Either success or fail
Console.WriteLine("Deposit successfully");
break;
case "3":
break;
default:
break;
}

}
}
}

我可以成功查询余额。对于选项 2,我可以毫无错误地执行“存款”选项。但是在数据库中,我的余额余额没有更新。事务也没有添加到数据库中。

如果我在 UpdateBankAccount 方法中放回 context.SaveChanges(); ,它会起作用。它返回 1。但是,我使用 UoW 来执行 SaveChanges()SaveChanges() 确实在 UoW Commit 方法中执行,但数据库未反射(reflect)其更改。 UoW Commit 方法 SaveChanges 返回 0。

完整代码可以在 Github repository 上找到.

最佳答案

这里问题的核心是,正在创建 AppDbContext 的两个实例来执行一个操作。在一个实例中进行了更改,在另一实例中调用了 SaveChanges。显然,它没有反射(reflect)在底层数据库中。

我们现在将从下到上逐步检查您的代码。

ATM.ConsoleUICore.Program.Main()方法中,注意以下代码:

AccountService accountService = new AccountService();
...
...
...
accountService.DepositAmount(bankAccount, 50);

您正在创建 AccountService 的实例。在 AccountService 的构造函数中,您正在创建 UnitOfWork 的实例,如下所示:

private readonly UnitOfWork uow;
public AccountService()
{
uow = new UnitOfWork();
}

UnitOfWork 的构造函数中,您正在创建 AppDbContext 的实例(它派生自 DbContext)。
您还有 BankAccounts 属性,它是 RepositoryBankAccount 的一个实例,如下所示:

private readonly AppDbContext db;
public UnitOfWork()
{
db = new AppDbContext();
}
...
...
...
private RepositoryBankAccount _BankAccounts;
public RepositoryBankAccount BankAccounts
{
get
{
if (_BankAccounts == null)
{
_BankAccounts = new RepositoryBankAccount();
}
return _BankAccounts;
}
}

现在的问题...

RepositoryBankAccount 的构造函数中,您再次创建了一个 AppDbContext 的实例,如下所示:

public AppDbContext context { get; }
public RepositoryBankAccount()
{
context = new AppDbContext();
}

实际上,您假装在一个 UnitOfWork 实例下的操作作为一个数据库事务执行。但是,当您在存储库中创建 AppDbContext 的不同实例时,情况并非如此。您的工作单元与存储库分离。你必须连接它们。到处都是 AppDbContext 的相同实例。

那么,解决方案是什么?

不要在任何存储库中创建 AppDbContext 的实例。相反,从工作单元注入(inject)现有实例。

public AppDbContext context { get; }

public RepositoryBankAccount(AppDbContext appDbContext)//<==Inject the AppDbContext
{
context = appDbContext;//<==Do NOT create new instance here; assign the injected instance.
}

然后,在您的 UnitOfWork 类中,更改属性 BankAccounts,如下所示:

private RepositoryBankAccount _BankAccounts;
public RepositoryBankAccount BankAccounts
{
get
{
if (_BankAccounts == null)
{
_BankAccounts = new RepositoryBankAccount(db);//<==Note that `db` means `AppDbContext` is injected
}
return _BankAccounts;
}
}

顺便说一下,避免所有这些不必要的包装器覆盖包装器。

看看这个answer这就解释了为什么不需要这样的包装器。

以防万一您决定继续现有设计,我已经在上面建议了一个解决方案。

此外,我建议您的一个工作单元应该是一个数据库事务。因此,您的数据库事务在您创建工作单元实例时开始,并在您处置它时结束(提交或回滚)。要么将所有内容都刷新到数据库,要么不刷新。在此期间发生的一切都应该是一个数据库事务的一部分。如果出现异常,则将工作单元一起回滚。

关于c# - 为什么数据库数据没有被更新但对象却没有错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57555384/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com