使用UnitOfWork存储库和带有AutoMapper的服务进行EFCore更新 [英] EFCore Update using UnitOfWork Repository and Service with AutoMapper

查看:365
本文介绍了使用UnitOfWork存储库和带有AutoMapper的服务进行EFCore更新的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Following the N-tier architecture, I'm currently having issues while trying to update an entity. In my Controller, if I set the properties manually, the update works perfectly, if I set the properties mapping the ViewModel into the entity it generate the exception "...cannot track multiple objects with the same key". How can I solve this?

这是我的UnitOfWork:

This is my UnitOfWork:

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:

This is my Generic Repository + 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();
}

最后,这是我的控制者:

And finally, This is my Controller:

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 在成功获取后会跟踪您的实体,因此您将最终遇到异常,因为您必须使用相同的键实例(已经跟踪了一个)

It doesn't work in your case because you are using IMapper.Map overload which creates a new instance of the currency object. Because of the fact that your GetCurrencyById track your entity after successful fetch, you'll end up with the exception since you'll have to instances with the same key (One tracked already in DbContext, and new one which mapper created).

您可以做两件事来阻止它:

There are two things you can do to prevent it:


  1. 从数据库中获取时使用 AsNoTracking()方法

  2. 使用 IMapper.Map 重载,同时使用目标实例和源实例的货币

  1. Use AsNoTracking() method when fetching from database
  2. Use IMapper.Map overload which takes both destination and source instances of currency:

_mapper.Map(currency,currencyToUpdate);

_mapper.Map(currency, currencyToUpdate);

这篇关于使用UnitOfWork存储库和带有AutoMapper的服务进行EFCore更新的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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