You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中区分Null值与缺失值的现有类型查询(Partial Update API)

ASP.NET Core 部分更新API的三种状态区分方案

在开发Partial Update API时,要区分「有值」「null值」「未指定」三种状态,ASP.NET Core里有几种现成方案不用自己造轮子:

1. 用JsonNode手动解析(无依赖轻量方案)

直接用System.Text.Json.Nodes.JsonNode接收请求体,通过判断属性是否存在来区分状态:

[HttpPatch("/api/entities/{id}")]
public IActionResult PatchEntity(int id, JsonNode patchData)
{
    var patchObj = patchData.AsObject();
    
    // 检查kind是否被指定(不管值是null还是具体内容)
    if (patchObj.ContainsKey("kind"))
    {
        string? kindValue = patchObj["kind"]?.GetValue<string>();
        // 执行kind字段更新逻辑:kindValue为null时重置,有值时替换
    }

    // 同理处理description
    if (patchObj.ContainsKey("description"))
    {
        string? descValue = patchObj["description"]?.GetValue<string>();
        // 执行description字段更新逻辑
    }

    return Ok();
}

这种方式不用额外定义类型,适合字段较少的场景,缺点是需要手动处理每个属性。

2. 使用第三方Optional<T>库(成熟方案)

可以用NuGet上的Optional包,它已经实现了能区分三种状态的Optional<T>类型:

  • 先安装包:Install-Package Optional
  • 定义Patch用的DTO:
public class EntityPatchDto
{
    public Optional<string> Kind { get; set; }
    public Optional<string> Description { get; set; }
}
  • 在Action中使用:
[HttpPatch("/api/entities/{id}")]
public IActionResult PatchEntity(int id, EntityPatchDto patchDto)
{
    // HasValue为true表示该属性被指定(不管值是null还是具体内容)
    if (patchDto.Kind.HasValue)
    {
        string? kind = patchDto.Kind.ValueOr(null);
        // 更新kind字段
    }

    if (patchDto.Description.HasValue)
    {
        string? description = patchDto.Description.ValueOr(null);
        // 更新description字段
    }

    return Ok();
}

这个库会自动处理JSON序列化逻辑:未提交的属性会是Optional<T>.None,提交null则是Optional<T>.Some(null),提交具体值就是Optional<T>.Some(具体值),完美区分三种状态。

3. 自定义极简版Optional<T>(无第三方依赖)

如果不想引入外部包,可以自己写一个轻量的Optional<T>,配合自定义JSON转换器:

// 定义Optional<T>结构体
public struct Optional<T>
{
    public bool HasValue { get; }
    public T Value { get; }

    private Optional(T value)
    {
        HasValue = true;
        Value = value;
    }

    public static Optional<T> Some(T value) => new(value);
    public static Optional<T> None => new();
}

// 自定义JSON转换器
public class OptionalJsonConverter<T> : JsonConverter<Optional<T>>
{
    public override Optional<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var value = JsonSerializer.Deserialize<T>(ref reader, options);
        return Optional<T>.Some(value);
    }

    public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options)
    {
        if (value.HasValue)
            JsonSerializer.Serialize(writer, value.Value, options);
        else
            writer.WriteNullValue();
    }
}

然后在Program.cs里注册转换器:

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // 为需要的类型添加转换器,比如string、int等
        options.JsonSerializerOptions.Converters.Add(new OptionalJsonConverter<string>());
        options.JsonSerializerOptions.Converters.Add(new OptionalJsonConverter<int?>());
    });

使用方式和第三方库一致,DTO中用Optional<T>作为属性类型即可。


内容的提问来源于stack exchange,提问作者Dirk Boer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 11:42:23