EF6中排除虚拟属性序列化且保留导航访问的解决方案
解决思路:拆分职责,分开处理EF映射与序列化排除
这个问题我之前也踩过坑!核心是你把EF数据库映射排除和JSON序列化排除的职责搞混了——[NotMapped]是给EF Core用的,用来标记「不需要映射到数据库列」的属性;但你加了这个特性后,EF会直接忽略这些虚拟属性,自然就失去了导航访问的能力。
正确的做法是:移除[NotMapped],改用序列化框架专属的忽略特性,让EF正常识别导航属性的同时,序列化时自动排除它们。
具体解决方案
1. 如果你用Newtonsoft.Json(Json.NET)
这是.NET生态最常用的JSON序列化库,直接给导航属性加上[JsonIgnore]特性即可:
public class Foo{ [Key] [Column("ENT_ID")] public int Id { set; get; } // 其他属性... // 移除[NotMapped],添加Json.NET的忽略特性 [JsonIgnore] public virtual Employee Employee{ set; get; } [JsonIgnore] public virtual List<Car> Cars{ set; get; } [JsonIgnore] public virtual List<Book> Books{ set; get; } public Foo() { Books= new List<Book>(); Cars= new List<Car>(); } }
这样EF会正常把这些属性当作导航属性,支持延迟加载/急加载;而序列化时会自动跳过它们。
2. 如果你用System.Text.Json(.NET Core 3.0+内置)
用.NET内置的序列化库的话,需要使用System.Text.Json.Serialization命名空间下的[JsonIgnore]:
using System.Text.Json.Serialization; public class Foo{ [Key] [Column("ENT_ID")] public int Id { set; get; } // 其他属性... // 注意是System.Text.Json专属的JsonIgnore [JsonIgnore] public virtual Employee Employee{ set; get; } [JsonIgnore] public virtual List<Car> Cars{ set; get; } [JsonIgnore] public virtual List<Book> Books{ set; get; } public Foo() { Books= new List<Book>(); Cars= new List<Car>(); } }
效果和上面一致:EF正常处理导航,序列化时排除属性。
3. 全局配置(不想逐个加特性的场景)
如果你有大量虚拟导航属性需要排除,可以全局配置序列化器,自动忽略所有虚拟属性:
对于Newtonsoft.Json:
在项目启动配置中添加全局规则:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new DefaultContractResolver { // 忽略所有虚拟属性 ShouldSerialize = (member) => !member.IsVirtual } };
对于System.Text.Json:
可以自定义转换器实现全局忽略虚拟属性:
// 全局配置时使用 var jsonOptions = new JsonSerializerOptions { Converters = { new IgnoreVirtualPropertiesConverter() } }; // 自定义转换器实现 public class IgnoreVirtualPropertiesConverter : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) => typeToConvert.IsClass; public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) { return (JsonConverter)Activator.CreateInstance( typeof(IgnoreVirtualPropertiesConverter<>).MakeGenericType(typeToConvert), args: new object[] { options })!; } } public class IgnoreVirtualPropertiesConverter<T> : JsonConverter<T> { private readonly JsonConverter<T> _defaultConverter; private readonly HashSet<string> _virtualPropertyNames; public IgnoreVirtualPropertiesConverter(JsonSerializerOptions options) { _defaultConverter = (JsonConverter<T>)options.GetConverter(typeof(T)); _virtualPropertyNames = typeof(T).GetProperties() .Where(p => p.GetGetMethod()?.IsVirtual == true) .Select(p => p.Name) .ToHashSet(); } public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => _defaultConverter.Read(ref reader, typeToConvert, options); public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) { using var jsonDoc = JsonDocument.Parse(_defaultConverter.Write(value, options)); writer.WriteStartObject(); foreach (var property in jsonDoc.RootElement.EnumerateObject()) { if (!_virtualPropertyNames.Contains(property.Name)) property.WriteTo(writer); } writer.WriteEndObject(); } }
内容的提问来源于stack exchange,提问作者Alaa Almaghrabi
相关产品推荐
相关产品推荐

