.NET Core C#如何区分Model Binding自动赋值null与用户主动传null
解决方案
针对你需要区分Model Binding自动赋值null和用户主动传null的场景,有两种常用且简便的实现方案:
方案1:轻量快速实现(适合小型项目/单个模型使用)
利用System.Text.Json内置的[JsonExtensionData]特性,记录所有请求传入的JSON字段,直接通过字段是否存在做判断,无需额外自定义逻辑:
步骤1:修改模型类
public class Album { public int Songs { get; set; } public string Name { get; set; } public string Composer { get; set; } // 存储所有请求传入的JSON属性 [JsonExtensionData] private Dictionary<string, JsonElement> _requestFields; // 封装判断方法:参数填模型属性名即可 public bool IsPropertyProvided(string propertyName) { // 默认ASP.NET Core用驼峰序列化JSON,这里做大小写适配 var jsonFieldName = char.ToLowerInvariant(propertyName[0]) + propertyName[1..]; return _requestFields?.ContainsKey(jsonFieldName) ?? false; } }
步骤2:Controller中使用
[HttpPut] public HttpResponseMessage PutAlbum(Album album) { // 先查询数据库现有记录 var existingAlbum = _dbContext.Albums.Find(/* 你的查询条件 */); if (existingAlbum == null) return NotFound(); // 仅更新用户主动传入的字段 if (album.IsPropertyProvided(nameof(Album.Songs))) existingAlbum.Songs = album.Songs; if (album.IsPropertyProvided(nameof(Album.Name))) existingAlbum.Name = album.Name; // 主动传了composer=null也会进入该分支,覆盖原有值 if (album.IsPropertyProvided(nameof(Album.Composer))) existingAlbum.Composer = album.Composer; _dbContext.SaveChanges(); return Ok(); }
方案2:通用可复用实现(适合中大型项目/多模型使用)
自定义Optional<T>泛型结构配合JsonConverter,通过HasValue属性直接判断字段是否被传入,类型安全无需手写属性名字符串:
步骤1:定义通用结构和转换器
// 可空包装结构 [JsonConverter(typeof(OptionalJsonConverter))] public readonly struct Optional<T> { public Optional(T value) { HasValue = true; Value = value; } // 为true说明用户主动传入了该字段(哪怕值是null) public bool HasValue { get; } public T Value { get; } public static implicit operator Optional<T>(T value) => new(value); } // 转换器工厂 public class OptionalJsonConverter : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(Optional<>); } public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { var innerType = typeToConvert.GetGenericArguments()[0]; var converterType = typeof(OptionalJsonConverter<>).MakeGenericType(innerType); return (JsonConverter)Activator.CreateInstance(converterType)!; } } // 泛型转换器实现 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 new Optional<T>(value); } public override void Write(Utf8JsonWriter writer, Optional<T> value, JsonSerializerOptions options) { if (value.HasValue) JsonSerializer.Serialize(writer, value.Value, options); } }
步骤2:修改模型类
public class Album { public Optional<int> Songs { get; set; } public Optional<string> Name { get; set; } public Optional<string> Composer { get; set; } }
步骤3:Controller中使用
[HttpPut] public HttpResponseMessage PutAlbum(Album album) { var existingAlbum = _dbContext.Albums.Find(/* 查询条件 */); if (existingAlbum == null) return NotFound(); if (album.Songs.HasValue) existingAlbum.Songs = album.Songs.Value; if (album.Name.HasValue) existingAlbum.Name = album.Name.Value; // 主动传null也会触发更新 if (album.Composer.HasValue) existingAlbum.Composer = album.Composer.Value; _dbContext.SaveChanges(); return Ok(); }
两种方案都可以完美适配你的需求:用户主动传入null时会触发字段更新,未传入字段则保留数据库原有值
内容的提问来源于stack exchange,提问作者venkkata
相关产品推荐
相关产品推荐

