EF Core中如何查询抽象类的所有子类对象并包含全部子字段?
问题描述
抽象基类定义:
public abstract class Workout { public WorkoutType Type { get; set; } public List<Measurement> Measurements { get; set; } = new(); // Id's and stuff }
两个子类:
public class RunningWorkout : Workout { public double PersonalBest { get; set; } }
public class WeightWorkout : Workout { public int PersonalBest { get; set; } }
需要实现API端点返回用户所有训练记录,但目前只能拿到基类字段,试过继承相关方法但因为PersonalBest是原始类型没用,求解决办法。
可行解决方案
方案一:配置JSON序列化支持多态
如果用ASP.NET Core(默认用System.Text.Json),直接在Program.cs里配置多态映射,让序列化器识别子类并输出所有属性:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, new DefaultJsonTypeInfoResolver { Modifiers = { typeInfo => { if (typeInfo.Type == typeof(Workout)) { typeInfo.PolymorphismOptions = new JsonPolymorphismOptions { DerivedTypes = { new JsonDerivedType(typeof(RunningWorkout), nameof(RunningWorkout)), new JsonDerivedType(typeof(WeightWorkout), nameof(WeightWorkout)) }, // 可选:把类型鉴别器字段改成你想用的名字,默认是"$type" TypeDiscriminatorPropertyName = "WorkoutType" }; } } } }); });
配置后API返回的JSON会自动包含子类的PersonalBest等属性,同时带上类型标识字段区分训练类型。
方案二:用DTO(数据传输对象)映射
创建对应每个子类的DTO,在API层把实体转成DTO返回,这种方式能完全控制返回字段,还能避免暴露实体细节:
先定义DTO类:
public abstract class WorkoutDto { public WorkoutType Type { get; set; } public List<MeasurementDto> Measurements { get; set; } = new(); } public class RunningWorkoutDto : WorkoutDto { public double PersonalBest { get; set; } } public class WeightWorkoutDto : WorkoutDto { public int PersonalBest { get; set; } }
然后在API里做映射(手动或用AutoMapper都可以):
[HttpGet("user-workouts")] public IActionResult GetUserWorkouts(int userId) { var workouts = _dbContext.Workouts .Where(w => w.UserId == userId) .ToList(); // 前提是EF Core已经正确配置了继承映射,能加载子类数据 var workoutDtos = workouts.Select(w => { return w switch { RunningWorkout rw => new RunningWorkoutDto { Type = rw.Type, Measurements = rw.Measurements.Select(m => new MeasurementDto { /* 这里填Measurement到MeasurementDto的映射逻辑 */ }).ToList(), PersonalBest = rw.PersonalBest }, WeightWorkout ww => new WeightWorkoutDto { Type = ww.Type, Measurements = ww.Measurements.Select(m => new MeasurementDto { /* 映射逻辑 */ }).ToList(), PersonalBest = ww.PersonalBest }, _ => throw new NotSupportedException("不支持该训练类型") }; }).ToList(); return Ok(workoutDtos); }
方案三:先确保EF Core正确加载子类数据
如果用EF Core,必须先配置继承映射(TPH/TPT/TPCC),否则查询基类时EF不会加载子类属性。比如用最常用的TPH(表每层次结构):
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Workout>() .HasDiscriminator<WorkoutType>("WorkoutType") .HasValue<RunningWorkout>(WorkoutType.Running) .HasValue<WeightWorkout>(WorkoutType.Weight); }
配置后查询_dbContext.Workouts时,EF会自动加载对应子类的所有属性,再配合方案一的JSON序列化配置就能返回完整数据。
方案四:动态类型(仅临时场景,不推荐生产用)
如果只是临时测试,可以用dynamic包装实体,但这种方式没有类型安全,生产环境别用:
[HttpGet("user-workouts")] public IActionResult GetUserWorkouts(int userId) { var workouts = _dbContext.Workouts .Where(w => w.UserId == userId) .ToList(); var dynamicWorkouts = workouts.Select(w => (dynamic)w).ToList(); return Ok(dynamicWorkouts); }
内容的提问来源于stack exchange,提问作者Davrozz
相关产品推荐
相关产品推荐

