使用Automapper通过ID映射非关联属性的实现方法
实现AutoMapper从InitialDeviceLocation映射DeviceName和LocationName的方案
咱们一步步来搞定这个需求:你的InitialDeviceLocation实体只存了DeviceId和LocationId,现在要通过这两个ID关联查询出对应的名称,映射到目标DTO里。下面是具体实现步骤:
1. 定义目标DTO
先创建一个包含所需属性的DTO类,新增DeviceName和LocationName字段:
public class InitialDeviceLocationDto { public int Id { get; set; } public Guid CampaignId { get; set; } public int DeviceId { get; set; } public int LocationId { get; set; } // 需要新增的名称属性 public string DeviceName { get; set; } public string LocationName { get; set; } }
2. 准备查询名称的服务/仓储
你需要能通过ID获取设备、位置名称的服务,先定义两个接口:
public interface IDeviceRepository { Task<string> GetDeviceNameByIdAsync(int deviceId); // 其他仓储方法按需添加 } public interface ILocationRepository { Task<string> GetLocationNameByIdAsync(int locationId); // 其他仓储方法按需添加 }
然后实现这两个接口,比如用EF Core查询Device和Location实体的Name属性(具体实现根据你的数据库结构调整即可)。
3. 配置AutoMapper Profile
创建AutoMapper配置类,通过构造函数注入上面的仓储,在映射规则里指定如何获取名称:
public class MappingProfile : Profile { private readonly IDeviceRepository _deviceRepo; private readonly ILocationRepository _locationRepo; // 注入仓储服务 public MappingProfile(IDeviceRepository deviceRepo, ILocationRepository locationRepo) { _deviceRepo = deviceRepo; _locationRepo = locationRepo; CreateMap<InitialDeviceLocation, InitialDeviceLocationDto>() // 基础属性自动映射,无需额外配置 .ForMember(dest => dest.DeviceName, opt => opt.ResolveUsing(async src => await _deviceRepo.GetDeviceNameByIdAsync(src.DeviceId))) .ForMember(dest => dest.LocationName, opt => opt.ResolveUsing(async src => await _locationRepo.GetLocationNameByIdAsync(src.LocationId))); } }
4. 注册服务到DI容器
在Startup.cs(.NET 6+用Program.cs)里注册AutoMapper、仓储及实现类:
// .NET 3.x/5 Startup.cs示例 public void ConfigureServices(IServiceCollection services) { // 注册仓储 services.AddScoped<IDeviceRepository, DeviceRepository>(); services.AddScoped<ILocationRepository, LocationRepository>(); // 注册AutoMapper,指定配置类所在程序集 services.AddAutoMapper(typeof(MappingProfile)); // 其他必要服务注册(比如DbContext、Controllers) services.AddDbContext<YourDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddControllers(); } // .NET 6+ Program.cs示例 builder.Services.AddScoped<IDeviceRepository, DeviceRepository>(); builder.Services.AddScoped<ILocationRepository, LocationRepository>(); builder.Services.AddAutoMapper(typeof(MappingProfile)); // 其他配置...
5. 在业务逻辑中使用映射器
在Controller或业务服务中注入IMapper,执行异步映射:
[ApiController] [Route("api/[controller]")] public class InitialDeviceLocationsController : ControllerBase { private readonly IMapper _mapper; private readonly YourDbContext _context; public InitialDeviceLocationsController(IMapper mapper, YourDbContext context) { _mapper = mapper; _context = context; } [HttpGet] public async Task<ActionResult<IEnumerable<InitialDeviceLocationDto>>> GetAll() { var entities = await _context.InitialDeviceLocations.ToListAsync(); // 异步映射实体到DTO var dtos = await _mapper.MapAsync<IEnumerable<InitialDeviceLocationDto>>(entities); return Ok(dtos); } }
可选优化:用AutoMapper投影直接查询数据库
如果想避免先查实体再映射的开销,可以用ProjectTo直接在数据库层面完成投影,性能更优:
修改MappingProfile(无需注入仓储,前提是DbContext包含Devices和Locations集合):
public class MappingProfile : Profile { public MappingProfile() { CreateMap<InitialDeviceLocation, InitialDeviceLocationDto>() .ForMember(dest => dest.DeviceName, opt => opt.MapFrom(src => _context.Devices.FirstOrDefault(d => d.Id == src.DeviceId).Name)) .ForMember(dest => dest.LocationName, opt => opt.MapFrom(src => _context.Locations.FirstOrDefault(l => l.Id == src.LocationId).Name)); } }
查询时直接用ProjectTo:
[HttpGet] public async Task<ActionResult<IEnumerable<InitialDeviceLocationDto>>> GetAll() { var dtos = await _context.InitialDeviceLocations .ProjectTo<InitialDeviceLocationDto>(_mapper.ConfigurationProvider) .ToListAsync(); return Ok(dtos); }
内容的提问来源于stack exchange,提问作者TDC
相关产品推荐
相关产品推荐

