我尝试使用AutoMapper.Collection.EntityFrameworkCore映射对象。如果我一直使用相同的DbContext,那么一切都很好。
问题在于,无法拒绝DbContext中所有缓存的对象。是的,我进行了搜索,发现了this个帖子,但它不起作用。我不太了解这个问题,但我敢打赌,这是因为我仅分离了容器对象。没有复杂的算法就无法遍历所有对象以分离它们。
这是当前有效的代码(非常简单):
using var ctx = this.DbContextFactory.CreateDbContext();
var dtoProject = await ctx.Set<DtoProject>().Include(item => item.Jobs).FirstAsync();
var p = this.Mapper.Map<Project>(dtoProject);
var j = new Job(Guid.NewGuid().ToString("B").ToUpperInvariant(),$"Job {p.Jobs.Count + 1}");
p.Jobs.Add(j);
await ctx.Set<DtoProject>().Persist(this.Mapper).InsertOrupdateAsync(p);
await ctx.SaveChangesAsync();
此代码重用了ctx
上的SaveChangesAsync()
,该代码按预期工作。
但是,这导致一个DbContext
实例的寿命很长,因为只要使用业务对象,它就必须处于活动状态。这听起来不像是一个真正的问题,但是如果需要,我无法使DbContext
中的对象无效以强制重新加载。
它似乎要走的路是拥有一个短暂的DbContext
实例。听起来不错。我更改了上面的代码,以便使用单独的方法加载业务对象,并使用新的上下文来保存更改。
此简单代码显示更改:
using var ctx = this.DbContextFactory.CreateDbContext();
var dtoProject = await ctx.Set<DtoProject>().Include(item => item.Jobs).FirstAsync();
var p = this.Mapper.Map<Project>(dtoProject);
var j = new Job(Guid.NewGuid().ToString("B").ToUpperInvariant(),$"Job {p.Jobs.Count + 1}");
p.Jobs.Add(j);
using var tmpCtx = this.DbContextFactory.CreateDbContext();
await tmpCtx.Set<DtoProject>().Persist(this.Mapper).InsertOrupdateAsync(p);
await tmpCtx.SaveChangesAsync();
唯一的更改是一个名为DbContext
的新tmpCtx
,用于存储更改后的值。
但是此代码引发了一个DbUpdateException
,告诉我jobs.id
的UNIQUE约束违规。 “容器”实例p
的接缝被接受,但是包含的作业实例的接缝失败。
该如何解决?
以下代码显示了自动映射器的配置和对象声明:
private IMapper CreateMapper()
{
var mapperCfg = new MapperConfiguration(cfg =>
{
cfg.AddExpressionmapping();
cfg.AddCollectionmappers();
cfg.CreateMap<Job,DtoJob>()
.EqualityComparison((blo,dto) => blo.Id == dto.Id)
.ForMember(dst => dst.ParentId,opt => opt.Ignore())
.ForMember(dst => dst.Parent,opt => opt.Ignore())
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => src.Id))
.ForMember(dst => dst.Name,opt => opt.MapFrom(src => src.Name));
cfg.CreateMap<DtoJob,Job>()
.EqualityComparison((dto,blo) => dto.Id == blo.Id)
.ForCtorParam("id",opt => opt.MapFrom(src => src.Id))
.ForCtorParam("name",opt => opt.MapFrom(src => src.Name))
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => src.Name))
.ForSourceMember(src => src.ParentId,opt => opt.DoNotValidate())
.ForSourceMember(src => src.Parent,opt => opt.DoNotValidate());
cfg.CreateMap<Project,DtoProject>()
.EqualityComparison((blo,dto) => blo.Id == dto.Id)
.ForMember(dst => dst.Id,opt => opt.MapFrom(src => src.Name))
.ForMember(dst => dst.Jobs,opt => opt.MapFrom(src => src.Jobs));
cfg.CreateMap<DtoProject,Project>()
.EqualityComparison((dto,opt => opt.MapFrom(src => src.Jobs));
});
mapperCfg.AssertConfigurationIsValid();
return mapperCfg.CreateMapper();
}
public class Job
{
public Job(string id,string name)
{
this.Id = id;
this.Name = name;
}
public string Id { get; }
public string Name { get; }
}
public class Project
{
public Project(string id,string name)
{
this.Id = id;
this.Name = name;
}
public string Id { get; }
public string Name { get; }
public List<Job> Jobs { get; set; }
}
[Table("jobs")]
public class DtoJob
{
[Key]
[Column("id")]
public string Id { get; set; }
[Column("parent_id")]
[ForeignKey(nameof(Parent))]
[Required]
public string ParentId { get; set; }
public DtoProject Parent { get; set; }
[Column("name")]
[Required]
public string Name { get; set; }
}
[Table("projects")]
public class DtoProject
{
[Key]
[Column("id")]
[Required]
public string Id { get; set; }
[Column("name")]
[Required]
public string Name { get; set; }
public List<DtoJob> Jobs { get; set; }
}
这是一个非常简单的测试代码,用于隔离问题。