C#如何在DTO中合并多个不同类型列表为统一列表传递给前端
实现方案
一、DTO层设计
方案1:多态继承方案(类型安全,无冗余字段)
先定义资产类型枚举和公共基类:
// 资产类型枚举,用于前端快速识别资源类型 public enum LessonAssetType { Image = 1, InstructionCard = 2, // 后续新增类型直接在这里加枚举值即可 Video = 3, Audio = 4 } // 公共资产基类,所有资源类型继承此类 public abstract class LessonAssetBase { public long Id { get; set; } public LessonAssetType AssetType { get; protected set; } public int SortOrder { get; set; } // 排序字段,控制前端展示顺序 }
修改原有资产类继承基类:
public class LessonImageAsset : LessonAssetBase { public string Title { get; set; } public ImageDetailView Image { get; set; } public LessonImageAsset() { AssetType = LessonAssetType.Image; Image = new ImageDetailView(); } } public class LessonInstructionCardAsset : LessonAssetBase { public string Instructions { get; set; } public LessonInstructionCardAsset() { AssetType = LessonAssetType.InstructionCard; } }
最后修改返回的根DTO:
public class LessonDetailView : BaseResult { public long Id { get; set; } public string Title { get; set; } // 统一的资产列表,所有类型资源都放在这里 public List<LessonAssetBase> Assets { get; set; } = new(); }
注意:如果使用System.Text.Json序列化,需要配置多态序列化支持,示例配置如下:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); }); [JsonDerivedType(typeof(LessonImageAsset), typeDiscriminator: "image")] [JsonDerivedType(typeof(LessonInstructionCardAsset), typeDiscriminator: "instructionCard")] public abstract class LessonAssetBase { // 原有属性 } [JsonSerializable(typeof(LessonAssetBase))] [JsonSerializable(typeof(LessonImageAsset))] [JsonSerializable(typeof(LessonInstructionCardAsset))] public partial class AppJsonSerializerContext : JsonSerializerContext { }
方案2:平层统一DTO方案(序列化简单,前端适配成本低)
直接定义一个包含所有可能字段的统一资产类,通过类型字段标识当前资源类型:
public enum LessonAssetType { Image = 1, InstructionCard = 2, Video = 3 } public class LessonAssetsListView { public long Id { get; set; } public LessonAssetType AssetType { get; set; } public int SortOrder { get; set; } // 图片类型专属字段 public string Title { get; set; } public ImageDetailView Image { get; set; } // 操作指引卡专属字段 public string Instructions { get; set; } // 后续新增类型的字段直接在这里追加即可 } // 根DTO修改为 public class LessonDetailView : BaseResult { public long Id { get; set; } public string Title { get; set; } public List<LessonAssetsListView> Assets { get; set; } = new(); }
- 优点:无需处理多态序列化配置,前端无需判断不同结构,直接根据
AssetType读取对应字段即可 - 缺点:资产类型越多,冗余字段越多,不适合类型字段差异极大的场景
二、合并逻辑的实现位置
合并、排序逻辑完全放在Service层实现,DTO层只做数据载体,不包含任何业务逻辑:
// Service层示例代码 public async Task<LessonDetailView> GetLessonDetail(long lessonId) { var lesson = await _dbContext.Lessons.FindAsync(lessonId); // 分别查询各类资产 var images = await _dbContext.LessonImages .Where(x => x.LessonId == lessonId) .Select(x => new LessonImageAsset { Id = x.Id, Title = x.Title, Image = _mapper.Map<ImageDetailView>(x.Image), SortOrder = x.Sort }) .ToListAsync(); var cards = await _dbContext.LessonInstructionCards .Where(x => x.LessonId == lessonId) .Select(x => new LessonInstructionCardAsset { Id = x.Id, Instructions = x.Instructions, SortOrder = x.Sort }) .ToListAsync(); // 合并所有资产并按排序字段排序 var allAssets = new List<LessonAssetBase>(); allAssets.AddRange(images); allAssets.AddRange(cards); // 后续新增资产类型只需要在此处添加查询和合并逻辑即可 allAssets = allAssets.OrderBy(x => x.SortOrder).ToList(); return new LessonDetailView { Id = lesson.Id, Title = lesson.Title, Assets = allAssets }; }
内容的提问来源于stack exchange,提问作者Iain Watt
相关产品推荐
相关产品推荐

