C# AutoMapper.Collections,EFCore-使用短期DbContext

C# AutoMapper.Collections,EFCore-使用短期DbContext,c#,entity-framework-core,automapper,dbcontext,C#,Entity Framework Core,Automapper,Dbcontext,我尝试使用来映射我的对象。如果我一直使用相同的DbContext,那么一切都很好 问题是不可能拒绝DbContext中的所有缓存对象。是的,我做了一个搜索并找到了一个帖子,但它不起作用。我并不真正理解这个问题,但我打赌这是因为我只分离容器对象。如果没有复杂的算法,就无法遍历所有对象以将其全部分离 这是当前的工作代码(非常简单): 唯一的更改是一个名为tmpCtx的新DbContext,用于存储更改后的值 但是这段代码抛出了一个DbUpdateException,它告诉我jobs.id的唯一约束冲

我尝试使用来映射我的对象。如果我一直使用相同的DbContext,那么一切都很好

问题是不可能拒绝DbContext中的所有缓存对象。是的,我做了一个搜索并找到了一个帖子,但它不起作用。我并不真正理解这个问题,但我打赌这是因为我只分离容器对象。如果没有复杂的算法,就无法遍历所有对象以将其全部分离

这是当前的工作代码(非常简单):

唯一的更改是一个名为
tmpCtx
的新
DbContext
,用于存储更改后的值

但是这段代码抛出了一个
DbUpdateException
,它告诉我
jobs.id
的唯一约束冲突。“容器”实例
p
seam将被接受,但包含的作业实例seam将失败

如何解决这个问题?

以下代码显示automapper配置和对象声明:

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.Id))
            .ForMember(dst => dst.Name, 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.Id))
            .ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Name))
            .ForMember(dst => dst.Jobs, opt => opt.MapFrom(src => src.Jobs));
        cfg.CreateMap<DtoProject, Project>()
            .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.Id))
            .ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Name))
            .ForMember(dst => dst.Jobs, 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; }
}
private IMapper CreateMapper()
{
var mapperpcfg=新的MapperConfiguration(cfg=>
{
cfg.AddExpressionMapping();
cfg.AddCollectionMappers();
cfg.CreateMap()
.EqualityComparison((blo,dto)=>blo.Id==dto.Id)
.FormMember(dst=>dst.ParentId,opt=>opt.Ignore())
.FormMember(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()
.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.Id))
.ForMember(dst=>dst.Name,opt=>opt.MapFrom(src=>src.Name))
.ForSourceMember(src=>src.ParentId,opt=>opt.DoNotValidate())
.ForSourceMember(src=>src.Parent,opt=>opt.DoNotValidate());
cfg.CreateMap()
.EqualityComparison((blo,dto)=>blo.Id==dto.Id)
.ForMember(dst=>dst.Id,opt=>opt.MapFrom(src=>src.Id))
.ForMember(dst=>dst.Name,opt=>opt.MapFrom(src=>src.Name))
.ForMember(dst=>dst.Jobs,opt=>opt.MapFrom(src=>src.Jobs));
cfg.CreateMap()
.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.Id))
.ForMember(dst=>dst.Name,opt=>opt.MapFrom(src=>src.Name))
.ForMember(dst=>dst.Jobs,opt=>opt.MapFrom(src=>src.Jobs));
});
mapperCfg.AssertConfiguration验证();
返回mapperpfg.CreateMapper();
}
公开课工作
{
公共作业(字符串id、字符串名称)
{
这个.Id=Id;
this.Name=Name;
}
公共字符串Id{get;}
公共字符串名称{get;}
}
公共类项目
{
公共项目(字符串id、字符串名称)
{
这个.Id=Id;
this.Name=Name;
}
公共字符串Id{get;}
公共字符串名称{get;}
公共列表作业{get;set;}
}
[表(“工作”)]
公共类DtoJob
{
[关键]
[列(“id”)]
公共字符串Id{get;set;}
[列(“父项id”)]
[外键(母公司名称))]
[必需]
公共字符串ParentId{get;set;}
公共DtoProject父项{get;set;}
[列(“名称”)]
[必需]
公共字符串名称{get;set;}
}
[表(“项目”)]
公共类DtoProject
{
[关键]
[列(“id”)]
[必需]
公共字符串Id{get;set;}
[列(“名称”)]
[必需]
公共字符串名称{get;set;}
公共列表作业{get;set;}
}

这是一个非常简单的测试代码,用于隔离问题。

我找到了一个似乎可行的解决方案。步骤如下:

  • 负载:
    • 创建一个
      DbContext
      实例
    • 加载DTO并将其映射到业务逻辑实例
    • 拒绝
      DbContext
      实例
  • 修改业务逻辑实例(添加/修改/删除子项)
  • 保存:
    • 创建一个
      DbContext
      实例
    • 通过键将与业务对象对应的DTO加载到
      DbSet
      。返回值并不重要。我们只需要知道这个物体
    • 使用
      InsertOrUpdate
      保存更改后的业务逻辑实例
这执行了一个本不需要的加载操作,但我没有找到其他解决方案

简化的代码是:

var p = (await this.LoadAsync()).FirstOrDefault();
// null handling omitted
var j = new Job(Guid.NewGuid().ToString("B").ToUpperInvariant(), $"Job {p.Jobs.Count + 1}");

p.Jobs.Add(j);
p.Jobs.RemoveAt(0);

await this.SaveAsync(p);
这使用以下方法:

private async Task<IList<Project>> LoadAsync(Expression<Func<Project, bool>> filter = null)
{
    using var ctx = this.DbContextFactory.CreateDbContext();
    IQueryable<DtoProject> query = ctx.Set<DtoProject>().Include(item => item.Jobs);
    if (!(filter is null))
    {
        query = query.Where(this.Mapper.MapExpression<Expression<Func<DtoProject, bool>>>(filter));
    }

    var resultDtos = await query.ToListAsync();
    var result = resultDtos.Select(this.Mapper.Map<Project>).ToList();
    return result;
}

private async Task SaveAsync(Project project)
{
    using var ctx = this.DbContextFactory.CreateDbContext();
    await ctx.Set<DtoProject>().FindAsync(p.Id == project.Id);
    await ctx.Set<DtoProject>().Persist(this.Mapper).InsertOrUpdateAsync(projectToStore);
    await ctx.SaveChangesAsync();
}
private async Task LoadAsync(表达式过滤器=null)
{
使用var ctx=this.DbContextFactory.CreateDbContext();
IQueryable query=ctx.Set().Include(item=>item.Jobs);
如果(!(过滤器为空))
{
query=query.Where(this.Mapper.MapExpression(filter));
}
var resultDtos=await query.ToListAsync();
var result=resultDtos.Select(this.Mapper.Map.ToList();
返回结果;
}
专用异步任务SaveAsync(项目)
{
使用var ctx=this.DbContextFactory.CreateDbContext();
等待ctx.Set().FindAsync(p.Id==project.Id);
等待ctx.Set().Persist(this.Mapper).InsertOrUpdateAsync(projectToStore);
等待ctx.saveChangesSync();
}
此解决方案的主要优点是
DbContext
实例的寿命非常短。它们仅用于加载和保存对象。业务逻辑对象的生存期对
DbContext
实例没有任何影响

这就是我现在要尝试的方法,希望它能解决所有的问题

var p = (await this.LoadAsync()).FirstOrDefault();
// null handling omitted
var j = new Job(Guid.NewGuid().ToString("B").ToUpperInvariant(), $"Job {p.Jobs.Count + 1}");

p.Jobs.Add(j);
p.Jobs.RemoveAt(0);

await this.SaveAsync(p);
private async Task<IList<Project>> LoadAsync(Expression<Func<Project, bool>> filter = null)
{
    using var ctx = this.DbContextFactory.CreateDbContext();
    IQueryable<DtoProject> query = ctx.Set<DtoProject>().Include(item => item.Jobs);
    if (!(filter is null))
    {
        query = query.Where(this.Mapper.MapExpression<Expression<Func<DtoProject, bool>>>(filter));
    }

    var resultDtos = await query.ToListAsync();
    var result = resultDtos.Select(this.Mapper.Map<Project>).ToList();
    return result;
}

private async Task SaveAsync(Project project)
{
    using var ctx = this.DbContextFactory.CreateDbContext();
    await ctx.Set<DtoProject>().FindAsync(p.Id == project.Id);
    await ctx.Set<DtoProject>().Persist(this.Mapper).InsertOrUpdateAsync(projectToStore);
    await ctx.SaveChangesAsync();
}