EF查询Attachment模型后如何映射为AttachmentResponse类集合?
问题描述
我有两个不同的类:
以下是名为"Attachments"的数据库表对应的模型:
namespace WebApi.Models { public class Attachment { public enum EntityType { Transaction } public int AttachmentId { get; set; } public int EntityId { get; set; } public EntityType Type { get; set; } public string Filename { get; set; } public DateTime Created { get; set; } public DateTime Updated { get; set; } } }
我的dbContext类如下:
namespace WebApi.Models { public class accountingContext : DbContext { public DbSet<User>? Users { get; set; } public DbSet<Transaction>? Transactions { get; set; } public DbSet<Attachment>? Attachments { get; set; } ///... } }
另外我有一个与Attachment模型类似的类,用于Web API请求的响应返回,代码如下:
namespace WebApi.Entities.AttachmentResponse; public class AttachmentResponse { public int AttachmentId { get; set; } public string Filename { get; set; } }
出于某些原因,我希望将响应类与模型类分开,不使用模型类直接返回。在对模型类执行查询后,我需要将结果转换并映射到AttachmentResponse类。我尝试了以下代码:
List<WebApi.Entities.AttachmentResponse> attachments = ( from a in db.Attachments from t in db.Transactions.Where(t => a.EntityId == t.TransactionId && t.UserId == userId) where a.EntityId == EntityId select new { a.AttachmentId, a.Filename }).ToList();
但上述尝试出现错误。我也查看过ConvertAll方法,但不知道如何在上述代码中使用。请问是否可以实现我想要的功能?如果可以,我的代码哪里出错了?
解决方案
完全可以实现你的需求,你的代码主要存在两个问题:
- 类型不匹配:查询中
select new { ... }生成的是匿名类型,无法直接赋值给List<AttachmentResponse>,两者类型不兼容。 - 查询逻辑冗余且易出问题:
where a.EntityId == EntityId和交叉连接Transactions的过滤条件重复,且这种写法会导致同一个Attachment对应多个Transaction时返回重复数据。
正确实现方式
方式一:直接在LINQ查询中构造AttachmentResponse
EF Core支持直接在查询中实例化DTO类(只要类有公开无参构造函数且属性可设置),这样查询会直接返回目标类型,无需额外转换:
List<WebApi.Entities.AttachmentResponse> attachments = ( from a in db.Attachments where a.EntityId == EntityId && db.Transactions.Any(t => t.TransactionId == a.EntityId && t.UserId == userId) select new WebApi.Entities.AttachmentResponse { AttachmentId = a.AttachmentId, Filename = a.Filename }).ToList();
这里用Any替代交叉连接,既保证了过滤逻辑,又避免了重复数据,同时直接构造目标类型实例,解决类型不匹配问题。
方式二:先查匿名类型再转换(适合复杂场景)
如果需要先处理匿名类型再转换,或者无法直接在查询中构造DTO,可以用Select或ConvertAll完成转换:
// 使用Select转换 List<WebApi.Entities.AttachmentResponse> attachments = ( from a in db.Attachments where a.EntityId == EntityId && db.Transactions.Any(t => t.TransactionId == a.EntityId && t.UserId == userId) select new { a.AttachmentId, a.Filename }) .ToList() .Select(anon => new WebApi.Entities.AttachmentResponse { AttachmentId = anon.AttachmentId, Filename = anon.Filename }) .ToList(); // 使用ConvertAll转换 List<WebApi.Entities.AttachmentResponse> attachments = ( from a in db.Attachments where a.EntityId == EntityId && db.Transactions.Any(t => t.TransactionId == a.EntityId && t.UserId == userId) select new { a.AttachmentId, a.Filename }) .ToList() .ConvertAll(anon => new WebApi.Entities.AttachmentResponse { AttachmentId = anon.AttachmentId, Filename = anon.Filename });
注意:ConvertAll是List<T>的方法,必须先调用ToList()将查询结果加载到内存后才能使用。
额外提示
如果项目中有大量类似的DTO映射需求,建议使用AutoMapper这类映射库简化代码:
// 配置映射规则 var config = new MapperConfiguration(cfg => cfg.CreateMap<Attachment, AttachmentResponse>() .ForMember(dest => dest.AttachmentId, opt => opt.MapFrom(src => src.AttachmentId)) .ForMember(dest => dest.Filename, opt => opt.MapFrom(src => src.Filename))); var mapper = config.CreateMapper(); // 查询并自动映射 List<AttachmentResponse> attachments = db.Attachments .Where(a => a.EntityId == EntityId && db.Transactions.Any(t => t.TransactionId == a.EntityId && t.UserId == userId)) .ProjectTo<AttachmentResponse>(config) .ToList();
内容的提问来源于stack exchange,提问作者Musaffar Patel
相关产品推荐
相关产品推荐

