实体
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 中的服务 自动映射器版本 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.InvalidOperationException:无法跟踪实体类型“PurchaseReqCharge”的实例,因为已跟踪具有键值“{Id:3}”的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
我不知道我在这里做错了什么。
我不知道我在这里做错了什么
EF 使用 tracking 执行数据修改操作,当您获取数据进行更新时,它会被跟踪,但是 AutoMapper 在映射期间将使用新元素创建全新的集合,因此当您尝试保存更新的实体时,它将包含“重复”项目(从 EF 更改跟踪器的角度来看)。
AutoMapper.Collection
图书馆。例如,作为映射器级别的快速修复,类似以下内容应该有效:
builder.Services.AddAutoMapper(cfg => cfg.AddCollectionMappers(), typeof(PurchaseReq));
对于所有 Dto -> 实体映射:
CreateMap<PurchaseReqItemDto, PurchaseReqItem>()
.EqualityComparison((dto, item) => item.Id == dto.Id);
但是请考虑检查文档 -
Automapper.Collection.EntityFrameworkCore
有一套用于管理此类情况的特定方法。
总的来说,我认为创建与您的实体(包括关系周期)完全匹配的 DTO 并不是一个好主意。