Экземпляр типа сущности '' не может быть отслежен - PullRequest
1 голос
/ 13 июня 2019

Я отправляю запрос авторизации, в контроллере метода для авторизации, я пытаюсь обновить сущность для пользователя, который прошел авторизацию, но у меня есть ошибка:

Экземпляр типа сущности 'SUsers' не может быть отслежен, поскольку другой экземпляр со значением ключа '{Id: 1}' уже отслеживается. При подключении существующих объектов убедитесь, что подключен только один экземпляр объекта с данным значением ключа.

стек используется

asp core 2.2, spa, vue, pwa, jwt, automapper 8.8.4, Microsoft.EntityFrameworkCore 2.2.4

Версия

  • Чистое ядро ​​2.2
  • Microsoft.EntityFrameworkCore 2.2.4
  • Microsoft.EntityFrameworkCore.InMemory 2.2.4
  • Microsoft.EntityFrameworkCore.Design 2.2.4
  • Microsoft.EntityFrameworkCore.SqlServer 2.2.4

0, DI

    public static class StartupExtension
    {

    public static IServiceCollection AddDependencies(this IServiceCollection _iServiceCollection, IConfiguration AppConfiguration )
    {

              #region Data

              string ids = System.Guid.NewGuid().ToString();

            _iServiceCollection.AddDbContext<BaseDbContext, FakeDbContext>(opt =>
            {
                opt.UseInMemoryDatabase(ids);
            });

            _iServiceCollection.AddScoped<IBaseDbContext>(provider => provider.GetService<BaseDbContext>());

            #endregion

            #region AutoMapper

            var config = new MapperConfiguration(cfg => {
                cfg.AddMaps("PWSPA.WEB", "PWSPA.BLL");
            });

            config.AssertConfigurationIsValid();
            #endregion

            #region Repository

            _iServiceCollection.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
            _iServiceCollection.AddScoped<IUnitOfWork, UnitOfWork>();
            #endregion

            #region service

            #region mapper service
            _iServiceCollection.AddScoped(typeof(IGenericMapperService<,>), typeof(GenericMapperService<,>));
            _iServiceCollection.AddScoped(typeof(IMapperService), typeof(MapperService));
            #endregion
            _iServiceCollection.AddScoped<IAuthService, AuthService>();
            #endregion

            return _iServiceCollection;
    }

}

1. Api Controller

    public class AuthController : BaseApiController
    {
        private readonly ILogger _log;
        private readonly SecuritySettings _config;
        private readonly IUserVerify _signInMgr;
        private readonly IAuthService _iAuthService;

        [AllowAnonymous]
        [HttpPost("login")]
        public IActionResult Login([FromBody] RequestTokenApiModel model)
        {
            try
            {
                SUsersDTO user = null;

                user = _iAuthService.SingleOrDefault(u => 
    u.WindowsLogin.ToLower() == "guest");

                user.WindowsLogin = "guest";

                /*
                The instance of entity type 'SUsers' cannot be tracked 
    because another 
                instance with the key value '{Id: 1}' is already being 
    tracked. When 
                attaching existing entities, ensure that only one entity 
    instance with a 
                given key value is attached.
                */

                countUpdate = _iAuthService.Update(user);

            }
            catch (ArgumentException ex)
            {
                return BadRequest(ex.Message);
            }
            catch (Exception ex)
            {
                _log.LogError(ex, ex.Message);
                return StatusCode(500, ex.Message);
            }
        }
    }

2. Сервис

    public class AuthService : ServiceBase<SUsers, SUsersDTO>, IAuthService
    {

        public AuthService(IUnitOfWork uow, IMapperService MapperService) : base(uow, MapperService)
        {
            Repository.Query().Include(u => u.Role).Load();
        }
        ...
   }

 public class ServiceBase<TModel, TModelDTO> : IGenericService<TModelDTO> where TModel : class where TModelDTO : class
    {
        private readonly IUnitOfWork db;
        private readonly IMapperService _MapService;
        private readonly IGenericRepository<TModel> genericRepository;
        private readonly IGenericMapperService<TModel, TModelDTO> genericMapService;

        public ServiceBase(IUnitOfWork uow, IMapperService iMapperService)
        {
            _MapService = iMapperService;
            db = uow;
            genericRepository = uow.Repository<TModel>();
            genericMapService = _MapService.Map<TModel, TModelDTO>();
        }
        protected virtual Type ObjectType => typeof(TModel);
        protected virtual IGenericRepository<TModel> Repository => genericRepository;
        protected virtual IMapperService MapService => _MapService;
        protected virtual IGenericMapperService<TModel, TModelDTO> Map => genericMapService;
        protected virtual IUnitOfWork Database => db;

        ...
             public int Update(TModelDTO entityDto)
        {
            var entity = Map.For(entityDto);
            return Repository.Update(entity);
        }

}

3. Repos

    public class GenericRepository<TEntity> :
        IGenericRepository<TEntity> where TEntity : class
    {
        private readonly IBaseDbContext _context;
        private readonly IUnitOfWork _unitOfWork;
        private readonly string errorMessage = string.Empty;

        public GenericRepository(IBaseDbContext context, IMapper _iMapper) //: base(context, _iMapper)
        {
            _context = context;
            _unitOfWork = new UnitOfWork(context, _iMapper);
        }
        public Type ObjectType => typeof(TEntity);

        protected virtual IBaseDbContext DbContext => _context;

        protected virtual DbSet<TEntity> DbSet => _context.Set<TEntity>();
        ...
        public int Update(TEntity updated)
        {
            if (updated == null)
            {
                return 0;
            }

            DbSet.Attach(updated);
            _context.Entry(updated).State = EntityState.Modified;
            return Save();
        }
        ...
        private int Save()
        {
            try
            {
                return _unitOfWork.Commit();
            }
            catch (DbUpdateException e)
            {
                throw new DbUpdateException(e.Message, e);
            }
        }

4. UnitOfWork

  public class UnitOfWork : IUnitOfWork
    {
        private readonly IBaseDbContext _dbContext;
        private readonly Dictionary<Type, object> _repositories = new Dictionary<Type, object>();
        private readonly IMapper _iMapper;


        public Dictionary<Type, object> Repositories
        {
            get => _repositories;
            set => Repositories = value;
        }

        public UnitOfWork(IBaseDbContext dbContext, IMapper _iMapper)
        {
            _dbContext = dbContext;
            this._iMapper = _iMapper;
        }

        public IGenericRepository<TEntity> Repository<TEntity>() where TEntity : class
        {
            if (Repositories.Keys.Contains(typeof(TEntity)))
            {
                return Repositories[typeof(TEntity)] as IGenericRepository<TEntity>;
            }

            IGenericRepository<TEntity> repo = new GenericRepository<TEntity>(_dbContext, _iMapper);
            Repositories.Add(typeof(TEntity), repo);
            return repo;
        }

        public EntityEntry<TEintity> Entry<TEintity>(TEintity entity) where TEintity : class
        {
            return _dbContext.Entry(entity);
        }
        ...
}

исключение происходит в хранилище

        public int Update(TEntity updated)
        {
            if (updated == null)
            {
                return 0;
            }
           /*
on line DbSet.Attach(updated) an exception occurs
*/
            DbSet.Attach(updated);
            _context.Entry(updated).State = EntityState.Modified;
            return Save();
        }

Я думаю, что это связано с отображением в службе, которая использует хранилище

      public int Update(TModelDTO entityDto)
        {
            var entity = Map.For(entityDto);
            return Repository.Update(entity);
        }

Шаги для воспроизведения

  1. клон https://github.com/UseMuse/asp-core-2.2-clean.git
  2. решение для сборки, запустите progect PWSPA.WEB
  3. войти: логин - гость, пройти - любые чарты
  4. в api-контроллере AuthController, метод Login, строка исключения 90

Ожидаемое поведение:

обновление сущности

ошибка msg

Экземпляр типа сущности 'SUsers' не может быть отслежен, поскольку другой экземпляр со значением ключа '{Id: 1}' уже отслеживается. При подключении существующих объектов убедитесь, что подключен только один экземпляр объекта с данным значением ключа.

StackTrace

в Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap 1.ThrowIdentityConflict(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap 1.Add (ключ TKey, запись InternalEntityEntry, логическое обновлениеDuplicate) в Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking (запись InternalEntityEntry) в Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState (EntityState oldState, EntityState newState, Boolean acceptChanges) в Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction (узел EntityEntryGraphNode, логическая сила) в Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph [TState] (узел EntityEntryGraphNode, состояние TState, Func 3 handleNode) at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState) at PWSPA.DAL.Repositories.GenericRepository 1. Обновление (обновлено TEntity) в D: \ repos \ asp-core-2.2-clean2 \ PWSPA. \ Репозитории \ GenericRepository.cs: строка 99 в PWSPA.BLL.Services.ServiceBase`2.Update (TModelDTO entityDto) в D: \ repos \ asp-core-2.2-clean2 \ PWSPA.BLL \ Services \ ServiceBase.cs: строка 208 в PWSPA.API.Controllers.AuthController.Login (модель RequestTokenApiModel) в D: \ repos \ asp-core-2.2-clean2 \ PWSPA.WEB \ API \ AuthController.cs: строка 90

Ответы [ 2 ]

2 голосов
/ 13 июня 2019

Вы совершили здесь ошибку новичка, по сути, просто сбросив всю информацию, о которой можете думать, и все же, по иронии судьбы, вы упустили единственную часть, которая действительно имеет значение: код вашей _iAuthService.Размещать только код, который напрямую связан с проблемой.Если нам нужно что-то еще, мы всегда можем попросить об этом.И, в этом отношении, отправьте весь код, который имеет непосредственное отношение к проблеме.Если ошибка возникает из написанного вами пользовательского класса обслуживания, опубликуйте этот класс обслуживания.

Тем не менее полученная ошибка сводится к следующей ситуации.В какой-то момент вы запрашиваете сущность, которая добавляет ее в отслеживание объекта контекста.Затем вы позже попытаетесь обновить не отслеживаемую версию этого объекта, а не ту, которую вы запросили.Это может произойти из-за получения его из связывателя модели (т. Е. Это параметр в действии), буквального создания экземпляра с помощью new или простого использования другого экземпляра контекста для его извлечения (и сохранения его в другом экземпляре).

На основании предоставленного вами кода мои деньги указаны на последнем.Вероятно, вы неправильно обрабатываете контекст в своем классе обслуживания, и вы заставляете сущность изменять один экземпляр контекста и пытаетесь обновить его другим экземпляром контекста.Ваш контекст должен всегда быть внедрен, чтобы гарантировать, что вы всегда используете один и тот же экземпляр в течение всей жизни (запрос).Другими словами, если вы делаете using (var context = new MyContext()) или действительно любой new MyContext(), это ваша проблема.

0 голосов
/ 19 июня 2019

Поскольку я использую automapper, моя проблема была решена с помощью AutoMapper.Collection

моя решаемая проблема

1. DI и init AutoMapper

    //using AutoMapper;
    //using AutoMapper.Configuration;
    //using AutoMapper.EquivalencyExpression;
    //using AutoMapper.Extensions.ExpressionMapping;

    services.AddAutoMapper (assemblyes);

    MapperConfigurationExpression configExpression = new MapperConfigurationExpression ();
    configExpression.AddCollectionMappers ();
    configExpression.AddExpressionMapping ();
    configExpression.UseEntityFrameworkCoreModel <BaseDbContext> (services.BuildServiceProvider (). 
    CreateScope (). ServiceProvider);
    configExpression.AddMaps (assemblyes);
    Mapper.Initialize (configExpression);
    Mapper.Configuration.AssertConfigurationIsValid ();

2. Мои расширения для Репо


   //using AutoMapper.EntityFrameworkCore;
   //using Microsoft.EntityFrameworkCore;

    public static class RepoExtensions
    {
        public static TModel InsertOrUpdate<TModel, TModelDto>(this IRepository repository, TModelDto modelDto) where TModel : BaseEntity where TModelDto :
     BaseEntityDTO
        {
            TModel model = repository.DbSet<TModel>().Persist().InsertOrUpdate(modelDto);
            repository.Save();
            return model;
        }
        public static async Task<TModel> InsertOrUpdateAsync<TModel, TModelDto>(this IRepository repository, TModelDto modelDto) where TModel : BaseEntity where TModelDto :
      BaseEntityDTO
        {
            TModel model = repository.DbSet<TModel>().Persist().InsertOrUpdate(modelDto);
            await repository.SaveAsync();
            return model;
        }
    }

3. Пример обновления сущности сущностью в услуге

до

public int Update(TModelDTO entityDto)
{
    var entity = Map.For(entityDto);
    return Repository.Update(entity);
}

после

    public TModel Update(TModelDTO entityDto)
     {
         return   Repository.InsertOrUpdate<TModel, TModelDTO>(entityDto);
     } 

p.s хранилище, указанное в примере, не обновлялось

...