.net-core - 无法跟踪实体类型“公司”的实例,因为另一个具有键值的实例?
问题描述
我在下面收到错误:
无法跟踪实体类型“公司”的实例,因为已在跟踪另一个具有键值“{Id: 1}”的实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
这是我的背景:
public class Context: DbContext
{
public Context(DbContextOptions<Context> options) : base(options)
{
this.ChangeTracker.LazyLoadingEnabled = false;
}
public DbSet<Company> Companies { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
Seed.OnModelCreating(builder);
//builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}
存储库基础
public class EfRepository<T> : IAsyncRepository<T> where T : class
{
protected readonly Context_context;
public EfRepository(Context dbContext)
{
_context = dbContext;
}
public virtual T GetById(int id)
{
var keyValues = new object[] { id };
return _context.Set<T>().Find(keyValues);
}
public List<T> List(Expression<Func<T, bool>> filter = null)
{
return filter != null ? _context.Set<T>().Where(filter).ToList() : _context.Set<T>().ToList();
}
public int Count(Expression<Func<T, bool>> filter = null)
{
return filter != null ? _context.Set<T>().Where(filter).Count() : _context.Set<T>().Count();
}
public void Add(T entity)
{
_context.Set<T>().Add(entity);
_context.SaveChanges();
}
public void Update(T entity)
{
var model = _context.Entry(entity);
model.State = EntityState.Modified;
_context.SaveChanges();
}
public void Delete(T entity)
{
_context.Set<T>().Remove(entity);
_context.SaveChanges();
}
public IQueryable<T> GetQueryable(Expression<Func<T, bool>> filter = null)
{
return filter == null
? _context.Set<T>()
: _context.Set<T>().Where(filter);
}
public T Get(Expression<Func<T, bool>> filter = null)
{
return filter == null
? _context.Set<T>().FirstOrDefault()
: _context.Set<T>().FirstOrDefault(filter);
}
public T GetWithoutTracking(Expression<Func<T, bool>> filter = null)
{
return filter == null
? _context.Set<T>().AsNoTracking().FirstOrDefault()
: _context.Set<T>().AsNoTracking().FirstOrDefault(filter);
}
public EntityState GetEntityState(T entity)
{
return _context.Entry(entity).State;
}
}
public class CompanyRepository : EfRepository<Company>, ICompanyRepository
{
public CompanyRepository(Context dbContext) : base(dbContext)
{}
public IQueryable<CompanyDtoWithDetail> GetCompanyWithDetail(int? id = null)
{
return (from company in _context.Companies
join companyType in _context.Definitions on company.CompanyTypeId equals companyType.Id
join country in _context.Countries on company.CountryId equals country.Id
join city in _context.Cities on company.CityId equals city.Id
join district in _context.Districts on company.DistrictId equals district.Id
join customerGroup in _context.Definitions on company.CustomerGroupId equals customerGroup.Id
where id.HasValue ? company.Id == id : true
select new CompanyDtoWithDetail(company, _context.AuthorizedPersons.OrderBy(person => person.Id).FirstOrDefault(person => person.CompanyId == company.Id), companyType.Name, country.Name, city.Name, district.Name, customerGroup.Name)
);
}
}
业务层更新方法
[ValidationAspect(typeof(CompanyValidator), Priority = 1)]
public IDataResult<Company> Update(Company entity)
{
var check = _companyRepository.GetWithoutTracking(x => x.Id == entity.Id);
if (check == null)
return new ErrorDataResult<Company>(Messages.CompanyNotFound);
_companyRepository.Update(entity);
var result = _companyRepository.GetCompanyWithDetail(entity.Id).FirstOrDefault();
return new SuccessDataResult<Company>(result, Messages.CompanyUpdated);
}
API 控制器
[HttpPut("update")]
public IActionResult Update(Company company)
{
var result = _companyService.Update(company);
return StatusCode(result.GetStatusCode(), result);
}
启动.cs
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
// Auto Mapper Configurations
services.AddSingleton(new MapperConfiguration(mc =>
{
mc.AddProfile(new AutoMapperBusinessProfile());
}).CreateMapper());
services.AddDbContext<Context>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("Context"));
options.EnableSensitiveDataLogging(true);
}
);
services.AddDependencyResolvers(new ICoreModule[]
{
new CoreModule(),
});
public class CompanyDtoWithDetail : Company
{
public string CompanyTypeName { get; set; }
public string CountryName { get; set; }
public string CityName { get; set; }
public string DistrictName { get; set; }
public string CustomerGroupName { get; set; }
public AuthorizedPerson AuthorizedPerson { get; set; }
public CompanyDtoWithDetail(Company company, AuthorizedPerson authorizedPerson, string companyTypeName, string countryName, string cityName, string districtName, string customerGroupName)
{
this.Id = company.Id;
this.CompanyTypeId = company.CompanyTypeId;
this.CompanyTypeName = companyTypeName;
this.Code = company.Code;
this.CommercialTitle = company.CommercialTitle;
this.Explanation = company.Explanation;
this.CountryId = company.CountryId;
this.CountryName = countryName;
this.CityId = company.CityId;
this.CityName = cityName;
this.DistrictId = company.DistrictId;
this.DistrictName = districtName;
this.Address = company.Address;
this.ZipCode = company.ZipCode;
this.CustomerGroupId = company.CustomerGroupId;
this.CustomerGroupName = customerGroupName;
this.IsInBlackList = company.IsInBlackList;
this.AuthorizedPerson = authorizedPerson;
}
}
[ValidationAspect(typeof(CompanyValidator), Priority = 1)]
public IDataResult<CompanyDtoWithDetail> Update(Company entity)
{
var check = _companyRepository.GetWithoutTracking(x => x.Id == entity.Id);
if (check == null)
return new ErrorDataResult<CompanyDtoWithDetail>(Messages.CompanyNotFound);
var entityState1 = _companyRepository.GetEntityState(entity); //Detached
_companyRepository.Update(entity);
var entityState2 = _companyRepository.GetEntityState(entity); // Unchanged
var result = _companyRepository.GetCompanyWithDetail(entity.Id).FirstOrDefault();
var entityState4 = _companyRepository.GetEntityState((Company)result); //Detached
return new SuccessDataResult<CompanyDtoWithDetail>(result, Messages.CompanyUpdated);
}
解决方案
当您调用时,您的实体状态更改为已修改,然后未更改
_companyRepository.Update(entity);
在此处查看带注释的实现:
public void Update(T entity)
{
var model = _context.Entry(entity);
// state is now Modified. This supercedes the AsNoTracking()
model.State = EntityState.Modified;
_context.SaveChanges();
// state is now Unchanged here; it's now part of the tracking
}
然后当你打电话
var result = _companyRepository.GetCompanyWithDetail(entity.Id).FirstOrDefault();
您收到该错误是因为您试图将另一个实体(同一个)加载到上下文中,其中一个已存在具有上述未更改状态的实体。
您需要在更新后首先分离实体(将其状态设置为Detached)
context.Entry(entity).State = EntityState.Detached;
或者您需要完全使用新的上下文。你如何将它融入你的通用回购取决于你。实际上,您没有遵循典型的工作单元模式(source):
使用 Entity Framework Core (EF Core) 时的典型工作单元涉及(我的重点补充):
- 创建 DbContext 实例 - 通过上下文跟踪实体实例。实体被跟踪
- 从查询中返回
- 被添加或附加到上下文
- 根据需要对跟踪的实体进行更改以实施业务规则
- 调用 SaveChanges 或 SaveChangesAsync。EF Core 检测所做的更改并将其写入数据库。
- DbContext 实例被释放
当您在上下文仍然“活动”DbContext
之后使用 a 时,因为它的当前状态仍然处于活动状态;SaveChanges
如果你做了一些与当前状态不兼容的事情,比如尝试再次加载同一个实体,你会得到错误。
使用 DbContextFactory
上面引用的来源建议您在工作范围与 DbContext 生命周期不一致时使用 DbContext 工厂。您的案例符合条件,因为您在 之后工作SaveChanges
。
资源
- https://www.tektutorialshub.com/entity-framework-core/change-tracker-entity-states-in-entity-framework-core/
- https://docs.microsoft.com/en-us/ef/core/change-tracking/
总之,你有3个选择
- 在保存更改后和下次读取之前分离实体
- 使用与注入工厂不同的上下文
- 更改您的访问模式以不重新阅读
推荐阅读
- php - preg_match 与多个查找
- ruby-on-rails - Rails ActiveQuery 接口
- web-services - 在 ColdFusion 中创建服务
- javascript - 如何在角度 4 中设置授权标头(不在 Access-Control-Request-Headers 内)。它应该是授权:值
- r - 有条件地替换数组中的值
- ajax - 像 AJAX 和 Laravel 的系统
- angular - 如何以角度 4 发射事件发射器?
- javascript - React - 当搜索(过滤器)找不到它时卡住了
- java - 从 log4j 迁移到 log4j2 - FileAppender 设置新文件名和 acitvateOptions
- java - 如何覆盖“未找到该网址的网页”消息?