实体

public class PurchaseReq
{
    ...
    public string PrNumber { get; set; } // primary key
    public List<PurchaseReqItem> PurchaseReqItems { get; init; } = [];
    public List<PurchaseReqCharge> PurchaseReqCharges { get; init; } = [];
}

public class PurchaseReqItem
{
    ...
    public int Id { get; init; } // primary key
    public string PrNumber { get; set; } // foreign key
    public PurchaseReq PurchaseReq { get; init; } //navigation property
}

public class PurchaseReqCharge
{
    ...
    public int Id { get; init; } // primary key
    public string PrNumber { get; set; } // foreign key
    public PurchaseReq PurchaseReq { get; init; } //navigation property
}

Dtos

public class PurchaseReqDto
{
    ...
    public string PrNumber { get; set; }
    public List<PurchaseReqItemDto> PurchaseReqItems { get; init; } = [];
    public List<PurchaseReqChargeDto> PurchaseReqCharges { get; init; } = [];
}

public class PurchaseReqItemDto
{
    ...
    public int Id { get; init; }
    public string PrNumber { get; set; }
    public PurchaseReqDto PurchaseReq { get; init; }
}

public class PurchaseReqChargeDto
{
    ...
    public int Id { get; init; }
    public string PrNumber { get; set; }
    public PurchaseReqDto PurchaseReq { get; init; }
}

映射配置文件

public class PurchaseReqProfile : Profile
{
    public PurchaseReqProfile()
    {
        CreateMap<PurchaseReq, PurchaseReqDto>();
        
        CreateMap<PurchaseReqDto, PurchaseReq>()
            .ForMember(d => d.PurchaseReqItems, o => o.MapFrom(src => src.PurchaseReqItems))
            .ForMember(d => d.PurchaseReqCharges, o => o.MapFrom(src => src.PurchaseReqCharges));
    }
}

public class PurchaseReqItemProfile : Profile
{
    public PurchaseReqItemProfile()
    {
        CreateMap<PurchaseReqItem, PurchaseReqItemDto>();
        
        CreateMap<PurchaseReqItemDto, PurchaseReqItem>()
            .ForMember(d => d.TaxRate, o => o.Ignore());
    }
}

public class PurchaseReqChargeProfile : Profile
{
    public PurchaseReqChargeProfile()
    {
        CreateMap<PurchaseReqCharge, PurchaseReqChargeDto>();
        
        CreateMap<PurchaseReqChargeDto, PurchaseReqCharge>()
            .ForMember(d => d.OtherCharge, o => o.Ignore());
    }
}

AutoMapper添加到服务Program.cs AutoMapper版本13.0.1 builder.Services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

PurchaseReqRepo的数据

public async Task<PurchaseReq> GetPrForUpdateAsync(string prNumber)
    {
        ArgumentNullException.ThrowIfNull(prNumber);

        return await context.PurchaseReqs
            .Include(pr => pr.PurchaseReqItems)
            .Include(pr => pr.PurchaseReqCharges)
            .SingleOrDefaultAsync(pr => pr.PrNumber == prNumber);
    }

更新 API

[HttpPut]
[CanUserUpdate(RolesConstant.PurchaseReq)]
public async Task<IActionResult> UpdatePurchaseReq([FromBody] PurchaseReqDto purchaseReqDto)
{
    if (!short.TryParse(User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value,out var userId))
        return Unauthorized();
        
    var purchaseReq = await purchaseReqRepo.GetPrForUpdateAsync(purchaseReqDto.PrNumber);
    if (purchaseReq == null)
        return NotFound();
        
    if (purchaseReq.IsSubmitted)
        return BadRequest(string.Format($"PR ({purchaseReq.PrNumber}) was already submitted for approval and can't be updated."));
        
    mapper.Map(purchaseReqDto, purchaseReq, options => 
        options.BeforeMap((prDto, pr) =>
        {
            pr.RemoveUnMatchedItems(prDto.PurchaseReqItems);
            pr.RemoveUnMatchedCharges(prDto.PurchaseReqCharges);
        }));
        
    purchaseReq.UpdateBy(userId);
        
    await purchaseReqRepo.SaveChangesAsync();

    return NoContent();
}

当将purchaseReqDto映射到purchaseReq时,我得到错误:

AutoMapper,System. InvalidOperationExcept:实体的实例 无法跟踪类型"AcquiseReqCharge",因为另一个实例 已在跟踪具有密钥值"{Id:3}"的.当附接 现有实体,确保只有一个实体实例具有给定 附加了关键值.

我不知道我做错了什么.

推荐答案

我不知道我做错了什么

EF使用tracking执行数据修改操作,当您获取数据进行更新时,它会被跟踪,但AutoMapper在映射期间将创建带有新元素的全新集合,因此当您try 保存更新的实体时,它将包含"重复"项(从EF变更跟踪器的Angular 来看).

您可以try 查看AutoMapper.Collection图书馆.例如,作为映射器级别的快速修复,以下内容应该有效:

builder.Services.AddAutoMapper(cfg => cfg.AddCollectionMappers(), typeof(PurchaseReq));

对于所有Dto -Entity映射:

CreateMap<PurchaseReqItemDto, PurchaseReqItem>()
    .EqualityComparison((dto, item) => item.Id == dto.Id);

来自文档:

  • 如果ID匹配,则AutoMapper将将Order DTO映射到Order
  • 如果Order DTO存在而Order不存在,则AutoMapper将添加从Order DTO映射到集合的新Order
  • 如果Order存在而Order DTO不存在,则AutoMapper将从集合中删除Order

还要考虑判断文档-有Automapper.Collection.EntityFrameworkCore个文档有一套管理此类情况的具体方法.

附言

总的来说,我认为创建完全匹配您的实体(包括关系周期)的DTO不是一个好主意.

Csharp相关问答推荐

应用程序启动时出现错误:操作无法同时使用表单和SON主体参数

C#自定义字典与JSON(de—)serialize

如何使用XmlSerializer反序列化字符串数组?

限制特定REST API不被访问,但部署代码

模型绑定RazorPage表单

静态对象构造顺序

在实体框架中处理通用实体&S变更跟踪器

如何使用C#中的主构造函数功能使用多个构造函数?

持有者安全定义未显示在Swagger.NET 8中

如何捕获对ASP.NET核心应用程序的所有请求并将其发送到一个页面

C#带主体的主构造函数?

正在寻找新的.NET8 Blazor Web应用程序.如何将.js添加到.razor页面?

错误CS1061';AuthenticationBuilder';不包含AddOpenIdConnect的定义

如何在使用属性 Select 器时判断是否可以为空

我想根据姓氏按字母顺序对包含150行徽章编号、姓氏、名字、地址等的文件进行排序.e

如何消除Visual Studio错误,因为它不识别集合表达式的新C#12语法?

使用C#12中的主构造函数进行空判断

C#;AvaloniaUI;MVVM;当另一个窗口上的按钮被单击时,如何更新视图图像源?

C#-如何将int引用获取到byte[]

默认架构不存在EF核心迁移