EFCore 更新使用 UnitOfWork 存储库和服务与 AutoMapper

EFCore Update using UnitOfWork Repository and Service with AutoMapper

按照 N 层架构,我目前在尝试更新实体时遇到问题。在我的控制器中,如果我手动设置属性,更新工作完美,如果我设置将 ViewModel 映射到实体的属性,它会生成异常“...无法使用相同的键跟踪多个对象”。 我该如何解决?

这是我的工作单元:

public class UnitOfWork : IUnitOfWork
{
    private readonly CoreContext _context;
    private IGenericRepository<Currency> currencyRepository;

    private static string DataConnectionString => new DatabaseConfiguration().GetDataConnectionString();

    public UnitOfWork(CoreContext context)
    {
        var optionsBuilder = new DbContextOptionsBuilder<CoreContext>();
        optionsBuilder.UseSqlServer(DataConnectionString);
        _context = new CoreContext(optionsBuilder.Options);

    }

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

    public async Task CommitAsync()
    {
        await _context.SaveChangesAsync();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    public IGenericRepository<Currency> CurrencyRepository
    {
        get { return this.currencyRepository ?? (this.currencyRepository = new GenericRepository<Currency>(_context)); }
    }

}


public interface IUnitOfWork : IDisposable
{
    int Commit();
    Task CommitAsync();
    IGenericRepository<Currency> CurrencyRepository { get; }

}

这是我的通用存储库 + CurrencyRepository:

public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    internal CoreContext _context;
    internal DbSet<T> _entities;

    public GenericRepository(CoreContext context)
    {
        _context = context;
        _entities = context.Set<T>();
    }

    public virtual void Update(T entityToUpdate)
    {
        _context.Entry(entityToUpdate).State = EntityState.Modified;
    }

    public IEnumerable<T> GetBy(Expression<Func<T, bool>> predicate)
    {
        IEnumerable<T> query = _entities.Where(predicate).AsEnumerable();
        return query;
    }

}


public interface IGenericRepository<T> where T : class
{
    void Update(T entity);
    IEnumerable<T> GetBy(Expression<Func<T, bool>> predicate);

}

public class CurrencyRepository : GenericRepository<Currency>, ICurrencyRepository
{        
    public CurrencyRepository(CoreContext context)
    : base(context) {
    }
}

这是我的服务:

public class CurrencyService : ICurrencyService
{
    private readonly IUnitOfWork _unitOfWork;

    public void UpdateCurrency(Currency currency)
    {
        _unitOfWork.CurrencyRepository.Update(currency);
    }

    public Currency GetCurrencyById(int Id)
    {
        return _unitOfWork.CurrencyRepository.GetBy(x => x.CurrencyId == Id).Single();

    }

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

}

public interface ICurrencyService 
{
    Currency GetCurrencyById(int Id);
    void UpdateCurrency(Currency currency);

    int SaveChanges();
}

最后,这是我的控制器:

public class CurrencyController : Controller
{
    private readonly ICurrencyService _currencyService;
    private readonly IMapper _mapper;

    public CurrencyController(ICurrencyService currencyService, IMapper mapper)
        : base()
    {
        _currencyService = currencyService;
        _mapper = mapper;
    }


    [HttpPost]
    public ActionResult UpdateCurrency([DataSourceRequest] DataSourceRequest dsRequest, CurrencyViewModel currency)
    {
        if (currency != null && ModelState.IsValid)
        {
            var currencyToUpdate = _currencyService.GetCurrencyById(currency.CurrencyId);

            if (currencyToUpdate != null)
            {
                //UPDATE NOT WORKING
                //currencyToUpdate = _mapper.Map<CurrencyViewModel, Currency>(currency);

                //UPDATE WORKING
                currencyToUpdate.Description = currency.Description;
                currencyToUpdate.Code= currency.Code;
                currencyToUpdate.Symbol = currency.Symbol;

                _currencyService.UpdateCurrency(currencyToUpdate);
                _currencyService.SaveChanges();
            }
        }

        return Json(ModelState.ToDataSourceResult());
    }

它在您的情况下不起作用,因为您正在使用 IMapper.Map 重载创建 currency 对象的新实例。由于您的 GetCurrencyById 在成功获取后跟踪您的实体,您最终会遇到异常,因为您必须使用相同的密钥实例(一个已经在 DbContext 中跟踪,而映射器创建的新实例).

您可以采取两种措施来防止它:

  1. 从数据库获取时使用AsNoTracking()方法
  2. 使用 IMapper.Map 重载,它接受 currency:

    的目标和源实例

    _mapper.Map(货币, currencyToUpdate);