为什么数据库中的数据没有被更新但是对象却被更新并且没有错误? [英] Why the database data is not being updated but object did and without error?

查看:103
本文介绍了为什么数据库中的数据没有被更新但是对象却被更新并且没有错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have this bank ATM mock-up app which implements some Domain-Driven Design architecture and Unit of Work pattern.

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

This app have 3 basic functions:


  • 检查余额

  • 存款

  • 提现

这些是项目层:

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,我可以执行存款选项而不会出现任何错误。但是在数据库中,我的余额余额未更新。事务也不会添加到数据库中。

I could check balance successfully. For option 2, I can execute "Deposit" option without any error. But in the database, my balance balance is not being updated. Transaction is also not added into the db.

如果我放回 context.SaveChanges(); UpdateBankAccount 方法,它可以工作。它返回1。但是,我使用UoW执行 SaveChanges() SaveChanges()确实以UoW Commit方法执行,但数据库未反映其更改。 UoW 提交方法 SaveChanges 返回0。

If I put back context.SaveChanges(); in UpdateBankAccount method, it works. It returns 1. But, I use UoW to perform SaveChanges(). The SaveChanges() did executed in UoW Commit method but the database didn't reflect its changes. The UoW Commit method SaveChanges returns 0.

完成可以在 Github存储库中找到代码。

Complete code can be found on Github repository.

推荐答案

问题的核心在于,正在创建两个 AppDbContext 实例来执行一个动作。在一个实例中进行更改,而在另一个实例上调用 SaveChanges 。显然,它并没有反映在基础数据库中。

The core of the problem here is that, two instances of AppDbContext are being created to conduct one single action. Changes are made in one instance and SaveChanges is being called on other instance. Obviously, it is not being reflected in underlying database.

我们现在将逐步从下至上逐步执行您的代码。

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

In ATM.ConsoleUICore.Program.Main() method, note the following code:


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


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

You are creating an instance of AccountService. In constructor of AccountService, you are creating an instance of UnitOfWork as below:


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


UnitOfWork ,您正在创建 AppDbContext 的实例(从 DbContext 派生) 。

您还具有 BankAccounts 属性,该属性是 RepositoryBankAccount 的实例,如下所示:

In constructor of UnitOfWork, you are creating an instance of AppDbContext (which is derived from DbContext).
You also have BankAccounts property there which is an instance of RepositoryBankAccount as below:


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 的实例,如下所示:

In constructor of RepositoryBankAccount, you are again creating an instance of AppDbContext as below:


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


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

Actually, you are pretending that your actions under one UnitOfWork instance are being executed as one database transaction. But, as you are creating different instance of AppDbContext in repository, this is not the case. Your unit of work is detached from repository. You have to connect them. It should be same instance of AppDbContext everywhere.

那么,解决方案是什么? >

请勿在任何存储库中创建 AppDbContext 的实例。而是从工作单元中注入现有实例。

Do NOT create an instance of AppDbContext in any repository. Instead, inject the existing instance from unit of work.

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

Then, in your UnitOfWork class, change the property BankAccounts as below:

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






顺便说一下,请避免将所有这些不必要的包装器都放在包装器上。



看看这个答案解释了为什么不需要这种包装。


By the way, avoid all these unnecessary wrappers over wrappers.

Have a look at this answer that explains why such wrappers are not needed.

如果您决定继续使用现有设计,我已经建议了一种解决方案

Just in case you decide to go on with your existing design, I have already suggested a solution above.

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

Additionally, I will suggest your one unit of work should be one database transaction. So, your database transaction starts when you create an instance of unit of work and ends (commit or rollback) when you dispose it. Either everything flushes to database or none. Everything that happens in between this should be part of one database transaction. In case of exception, rollback the unit of work all together.

这篇关于为什么数据库中的数据没有被更新但是对象却被更新并且没有错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆