我有一个域实体
KycInformation
,我使用两个 DTO 来处理不同的场景:
KycInformationDto
用于创建操作(无需 ID)KycInformationDetailDto
用于获取/更新操作(包括 ID)这些 DTO 之间的唯一区别是:
KycInformationDetailDto
包含 Id
属性Documents
集合使用KycDocumentDetailDto
代替KycDocumentDto
减少代码重复同时保持创建和获取/更新 DTO 之间的分离的最佳方法是什么?我正在使用 AutoMapper 在这些类型之间进行映射。
public class KycInformation : BaseEntity
{
public required KycStatus Status { get; set; }
public required RiskRating RiskRating { get; set; }
public required AmlStatus AmlStatus { get; set; }
public required SanctionsCheckStatus SanctionsCheckStatus { get; set; }
public string? ComplianceNotes { get; set; }
public string? ParentCompany { get; set; }
public required Guid LegalEntityId { get; set; }
public required LegalEntity LegalEntity { get; set; }
public ICollection<KycDocument> Documents { get; set; } = new List<KycDocument>();
public void AddDocument(KycDocument document) => Documents.Add(document);
public void ClearDocuments() => Documents.Clear();
}
public record KycInformationDto
{
public required KycStatus Status { get; init; }
public required RiskRating RiskRating { get; init; }
public required AmlStatus AmlStatus { get; init; }
public required SanctionsCheckStatus SanctionsCheckStatus { get; init; }
public string? ComplianceNotes { get; init; }
public string? ParentCompany { get; init; }
public ICollection<KycDocumentDto> Documents { get; init; } = new List<KycDocumentDto>();
private class Mapping : Profile
{
public Mapping()
{
CreateMap<KycInformation, KycInformationDto>();
CreateMap<KycInformationDto, KycInformation>();
}
}
}
public record KycInformationDetailDto : KycInformationDto
{
public required Guid Id { get; init; }
public new ICollection<KycDocumentDetailDto> Documents { get; init; } = new List<KycDocumentDetailDto>();
private class Mapping : Profile
{
public Mapping()
{
CreateMap<KycInformation, KycInformationDetailDto>();
CreateMap<KycInformationDetailDto, KycInformation>();
}
}
}
public record KycDocumentDto
{
public required string DocumentType { get; init; }
public required string DocumentName { get; init; }
public string? DocumentDescription { get; init; }
public required string DocumentUrl { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<KycDocument, KycDocumentDto>();
CreateMap<KycDocumentDto, KycDocument>();
}
}
}
public record KycDocumentDetailDto : KycDocumentDto
{
public required Guid Id { get; init; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<KycDocument, KycDocumentDetailDto>();
CreateMap<KycDocumentDetailDto, KycDocument>();
}
}
}
在我看来,使用组合方法将是正确的选择,它将帮助您避免或减少重复,因此您不需要创建单独的 DTO 来创建和更新。并且您使用的是 EF Core,它将自行管理 id。当您调用
SaveChanges
时,EF Core 将处理 ID 分配,因此您不需要创建单独的 DTO 来排除 Id
。使用 public Guid? Id => IdComponent.Id;
因此您不需要创建单独的类型。
我已经准备好了示例演示:
KycInformationController.cs:
using KycInformationApi.DTOs;
using KycInformationApi.Services;
using Microsoft.AspNetCore.Mvc;
namespace KycInformationApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class KycInformationController : ControllerBase
{
private readonly KycInformationService _service;
public KycInformationController(KycInformationService service)
{
_service = service;
}
[HttpPost]
[ProducesResponseType(typeof(KycInformationDto), StatusCodes.Status201Created)]
public async Task<ActionResult<KycInformationDto>> CreateKyc([FromBody] KycInformationDto dto)
{
var createdKyc = await _service.CreateKycAsync(dto);
return CreatedAtAction(nameof(GetKycById), new { id = createdKyc.Id }, createdKyc);
}
[HttpGet("{id}")]
[ProducesResponseType(typeof(KycInformationDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<KycInformationDto>> GetKycById(Guid id)
{
var kyc = await _service.GetKycByIdAsync(id);
if (kyc == null)
{
return NotFound();
}
return Ok(kyc);
}
}
}
IdComponent.cs:
namespace KycInformationApi.DTOs
{
public record IdComponent
{
public Guid? Id { get; set; }
}
public record KycInformationDto
{
public IdComponent IdComponent { get; init; } = new IdComponent();
public required KycStatus Status { get; init; }
public required RiskRating RiskRating { get; init; }
public required AmlStatus AmlStatus { get; init; }
public required SanctionsCheckStatus SanctionsCheckStatus { get; init; }
public string? ComplianceNotes { get; init; }
public string? ParentCompany { get; init; }
public ICollection<KycDocumentDto> Documents { get; init; } = new List<KycDocumentDto>();
public Guid? Id => IdComponent.Id;
}
public record KycDocumentDto
{
public required string DocumentType { get; init; }
public required string DocumentName { get; init; }
public string? DocumentDescription { get; init; }
public required string DocumentUrl { get; init; }
}
}
KycInformation.cs:
namespace KycInformationApi.Models
{
public class KycInformation
{
public Guid Id { get; set; } = Guid.NewGuid();
public required KycStatus Status { get; set; }
public required RiskRating RiskRating { get; set; }
public required AmlStatus AmlStatus { get; set; }
public required SanctionsCheckStatus SanctionsCheckStatus { get; set; }
public string? ComplianceNotes { get; set; }
public string? ParentCompany { get; set; }
public ICollection<KycDocument> Documents { get; set; } = new List<KycDocument>();
}
public class KycDocument
{
public Guid Id { get; set; } = Guid.NewGuid();
public required string DocumentType { get; set; }
public required string DocumentName { get; set; }
public string? DocumentDescription { get; set; }
public required string DocumentUrl { get; set; }
}
}
KycInformationRepository.cs:
using KycInformationApi.Models;
using Microsoft.EntityFrameworkCore;
namespace KycInformationApi.Repositories
{
public class KycInformationRepository
{
private readonly ApplicationDbContext _context;
public KycInformationRepository(ApplicationDbContext context)
{
_context = context;
}
public async Task AddAsync(KycInformation kycInformation)
{
_context.KycInformations.Add(kycInformation);
await _context.SaveChangesAsync();
}
public async Task<KycInformation?> GetByIdAsync(Guid id)
{
return await _context.KycInformations
.Include(k => k.Documents)
.FirstOrDefaultAsync(k => k.Id == id);
}
}
}
KycInformationService.cs:
using AutoMapper;
using KycInformationApi.DTOs;
using KycInformationApi.Models;
using KycInformationApi.Repositories;
namespace KycInformationApi.Services
{
public class KycInformationService
{
private readonly KycInformationRepository _repository;
private readonly IMapper _mapper;
public KycInformationService(KycInformationRepository repository, IMapper mapper)
{
_repository = repository;
_mapper = mapper;
}
public async Task<KycInformationDto> CreateKycAsync(KycInformationDto dto)
{
var kycEntity = _mapper.Map<KycInformation>(dto);
await _repository.AddAsync(kycEntity);
// Retrieve saved entity with ID and map back to DTO
return _mapper.Map<KycInformationDto>(kycEntity);
}
public async Task<KycInformationDto?> GetKycByIdAsync(Guid id)
{
var kycEntity = await _repository.GetByIdAsync(id);
return kycEntity == null ? null : _mapper.Map<KycInformationDto>(kycEntity);
}
}
}
ApplicationDbContext.cs:
using KycInformationApi.Models;
using Microsoft.EntityFrameworkCore;
namespace KycInformationApi
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
public DbSet<KycInformation> KycInformations { get; set; } = null!;
public DbSet<KycDocument> KycDocuments { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<KycInformation>().HasKey(k => k.Id);
modelBuilder.Entity<KycDocument>().HasKey(d => d.Id);
}
}
}
KycMappingProfile.cs:
using AutoMapper;
using KycInformationApi.DTOs;
using KycInformationApi.Models;
namespace KycInformationApi
{
public class KycMappingProfile : Profile
{
public KycMappingProfile()
{
// Map between KycInformation and KycInformationDto, including reverse mapping
CreateMap<KycInformation, KycInformationDto>()
.ForPath(dest => dest.IdComponent.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Documents, opt => opt.MapFrom(src => src.Documents))
.ReverseMap(); // This will handle mapping in both directions
// Map between KycDocument and KycDocumentDto, including reverse mapping
CreateMap<KycDocument, KycDocumentDto>().ReverseMap();
}
}
}
程序.cs:
using KycInformationApi.Repositories;
using KycInformationApi.Services;
using KycInformationApi;
using Microsoft.EntityFrameworkCore;
using KycInformationApi.DTOs;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Register services
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseInMemoryDatabase("KycDb"));
builder.Services.AddAutoMapper(typeof(KycMappingProfile));
builder.Services.AddScoped<KycInformationRepository>();
builder.Services.AddScoped<KycInformationService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// Seed data
using (var scope = app.Services.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<KycInformationService>();
// Example usage: Creating and retrieving KYC info
var newKyc = new KycInformationDto
{
Status = KycStatus.Active,
RiskRating = RiskRating.Medium,
AmlStatus = AmlStatus.Clear,
SanctionsCheckStatus = SanctionsCheckStatus.None,
ComplianceNotes = "Initial compliance review",
ParentCompany = "Example Corp",
Documents = new List<KycDocumentDto>
{
new KycDocumentDto
{
DocumentType = "ID",
DocumentName = "Passport",
DocumentUrl = "http://example.com/doc.pdf"
}
}
};
var createdKyc = await service.CreateKycAsync(newKyc);
var logger = scope.ServiceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Created KYC ID: {Id}", createdKyc.Id);
}
app.Run();
因此,在上面的代码中,KycInformationService 处理 DTO 和实体之间的映射。KycInformationRepository 添加了处理 KycInformation.KycMappingProfile 的创建和按 id 获取的逻辑,我们配置了 AutoMapper 来处理启用了反向映射的 DTO 和模型之间的映射。