如何在ASP.NET Core Web API的JSON结果中仅返回接口成员
我有一个简单的API,用于从数据库返回行列表,代码如下:
[HttpGet] [Route("list/{count}")] public async Task<ActionResult<IEnumerable<IResult>>> GetRows(int count) { var list = await dbc.GetList(count); return new JsonResult( list.ToList<IResult>() ); }
数据库返回的行包含id、name、status等属性,而接口仅包含其中的子集,定义如下:
public class Result { int id { get; set; } string name { get; set; } int status { get; set; } } public interface IResult { int id { get; set; } string name { get; set; } }
我目前使用.ToList<IResult>()来确保JSON结果仅包含接口中的字段,请问有没有比先转换为List更优、更高效的实现方式?
下面几种方式都比直接转换List更高效,各有适用场景:
查询阶段直接投影(性能最优)
不要先加载完整的Result对象,而是在数据库查询时就只选择需要的字段,这样既减少数据库传输的数据量,又避免了后续的类型转换操作。如果你的dbc.GetList(count)是基于IQueryable的,可以直接修改查询逻辑:[HttpGet] [Route("list/{count}")] public async Task<ActionResult<IEnumerable<IResult>>> GetRows(int count) { var list = await dbc.Results // 假设这是你的DbSet .Take(count) .Select(r => new Result { id = r.id, name = r.name }) // 仅选择接口需要的字段 .Cast<IResult>() .ToListAsync(); return list; }部分ORM支持直接投影到接口,也可以简化为:
.Select(r => new IResult { id = r.id, name = r.name })这种方式从根源上减少了不必要的数据加载,性能提升最明显。
通过序列化配置过滤字段
如果你不想修改查询逻辑,可以直接配置JSON序列化器,让它只序列化IResult接口定义的属性,这样直接返回原始的Result集合即可,不需要转换类型:
以System.Text.Json为例:[HttpGet] [Route("list/{count}")] public async Task<ActionResult<IEnumerable<IResult>>> GetRows(int count) { var list = await dbc.GetList(count); return Json(list, new JsonSerializerOptions { Converters = { new InterfaceProjectionConverter<IResult>() } }); } // 自定义转换器,仅序列化接口属性 public class InterfaceProjectionConverter<TInterface> : JsonConverter<TInterface> { public override TInterface Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { throw new NotImplementedException(); } public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options) { var interfaceProps = typeof(TInterface).GetProperties(); writer.WriteStartObject(); foreach (var prop in interfaceProps) { writer.WritePropertyName(prop.Name); JsonSerializer.Serialize(writer, prop.GetValue(value), options); } writer.WriteEndObject(); } }用Newtonsoft.Json的话,可以通过自定义
ContractResolver实现同样的效果,同样无需转换集合。使用AutoMapper的ProjectTo(适合多场景复用)
如果项目中已经在用AutoMapper,可以用ProjectTo<IResult>()方法,它会在IQueryable层面进行投影,只查询需要的字段,同时自动映射到接口类型:[HttpGet] [Route("list/{count}")] public async Task<ActionResult<IEnumerable<IResult>>> GetRows(int count) { var list = await dbc.GetListQueryable(count) // 需要返回IQueryable<Result> .ProjectTo<IResult>(_mapper.ConfigurationProvider) .ToListAsync(); return list; }这种方式适合需要在多个地方复用相同映射规则的场景,代码更简洁可维护。
内容的提问来源于stack exchange,提问作者Kumar

