繁体   English   中英

实体类型“”的实例无法跟踪

[英]The instance of entity type '' cannot be tracked

我正在发送授权请求,在用于授权的方法控制器中,我试图为已通过授权的用户更新实体,但出现错误:

无法跟踪实体类型“ 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控制器

    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.回购

    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.工作单位

  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

预期行为:

实体更新

错误味精

无法跟踪实体类型“ SUsers”的实例,因为已经跟踪了另一个键值为“ {Id:1}”的实例。 附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

堆栈跟踪

在Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap 1.ThrowIdentityConflict(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap处的1.ThrowIdentityConflict(InternalEntityEntry entry) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap Microsoft的Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState,EntityState newState,Boolean acceptChanges)在Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode节点,布尔值) EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph [TState]( 3 handleNode) at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState) at PWSPA.DAL.Repositories.GenericRepository 1上的EntityEntryGraphNode节点,TState状态,Func 3 handleNode) at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState) at PWSPA.DAL.Repositories.GenericRepository .Update(已更新TEntity) 在D:\\ repos \\ asp-中的D:\\ repos \\ asp-core-2.2-clean2 \\ PWSPA.DAL \\ Repositories \\ GenericRepository.cs:PWSPA.BLL.Services.ServiceBase`2.Update(TModelDTO entityDto)的第99行D:\\ repos \\ asp-core-2.2-clean2 \\ PWSPA.WEB \\中的PWSPA.API.Controllers.AuthController.Login(RequestTokenApiModel模型)处的core-2.2-clean2 \\ PWSPA.BLL \\ Services \\ ServiceBase.cs:第208行API \\ AuthController.cs:第90行

您在这里犯了一个新手错误,即本质上只是转储您可以想到的所有信息,但是,具有讽刺意味的是,您错过了唯一真正重要的部分: _iAuthService背后的代码。 仅发布与问题直接相关的代码。 如果我们需要其他东西,我们总是可以要求它。 并且,就此而言,发布与该问题直接相关的所有代码。 如果错误是由于您编写的自定义服务类引起的,请发布该服务类。

也就是说,您遇到的错误归结为以下情况。 有时,您查询一个实体,将其添加到上下文的对象跟踪中。 然后,您稍后尝试更新该实体的非跟踪版本,而不是查询的版本。 这可能是因为从模型绑定器接收到了它(即,这是动作的一个参数),实际上是使用new实例化了它,或者只是使用了上下文的另一个实例来检索它(并将其保存到另一个实例中)。

根据您提供的代码,我的钱就放在最后一个。 您可能没有在服务类中正确处理上下文,并且正在使实体从上下文的一个实例进行修改,并尝试使用上下文的另一个实例进行更新。 应该始终注入上下文,以确保在整个生命周期(请求)中始终使用同一实例。 换句话说,如果您正在using (var context = new MyContext())或实际上是任何new MyContext() ,那就是您的问题。

由于我使用automapper,因此我的问题已使用AutoMapper.Collection解决。

我的解决问题

1. DI和初始化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);
     } 

ps该示例引用的存储库未更新

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM