如何根据数据库Type字段动态设置泛型Post<T>的Content属性类型?
根据Type字段动态反序列化Post的Content属性类型
问题背景
我设计了如下泛型类:
public class Post<T>: BaseEntity where T : IPostContent { ... public required string Type { get; set; } ... public T Content { get; set; } }
其中IPostContent是接口,被Media、News等带有不同内容属性的类实现。
我的数据库Post表结构如下,Content字段存储JSON字符串:
CREATE TABLE Post ( [Id] BIGINT IDENTITY(1,1) PRIMARY KEY, ... [Type] NVARCHAR(50) NOT NULL, [Content] NVARCHAR(MAX) NULL, ... );
读取数据库时,能否通过Type字段设置Content属性的类型?具体如何实现?
我当前用了一种类型松散的替代方案,但感觉不规范:
var post = new DataModel.Post.Post() { ... Type = reader["Type"].ToString(), Content = reader["Content"] != DBNull.Value ? JsonSerializer.Deserialize<JsonNode>(reader["Content"].ToString()) : null, ... };
解决方案
可以实现,核心思路是建立Type字段值与IPostContent实现类的映射关系,再动态完成JSON反序列化。
步骤1:定义类型映射字典
先把Type字段的字符串值和对应的实体类做绑定,避免硬编码判断:
private static readonly Dictionary<string, Type> _contentTypeMap = new() { { "Media", typeof(Media) }, { "News", typeof(News) } // 后续新增的IPostContent实现类都在这里添加映射 };
步骤2:动态反序列化并构建对象
方案A:使用非泛型Post基类(推荐)
如果可以接受统一返回非泛型的基类,先定义一个非泛型的Post类:
public class Post : BaseEntity { public required string Type { get; set; } public IPostContent? Content { get; set; } // 其他公共字段 }
然后读取数据时处理:
string postType = reader["Type"].ToString()!; string? contentJson = reader["Content"] != DBNull.Value ? reader["Content"].ToString() : null; IPostContent? content = null; if (_contentTypeMap.TryGetValue(postType, out var targetType) && !string.IsNullOrEmpty(contentJson)) { content = (IPostContent)JsonSerializer.Deserialize(contentJson, targetType)!; } var post = new Post { // 赋值Id等其他字段 Type = postType, Content = content };
方案B:实例化泛型Post(反射方式)
如果必须使用泛型Post<T>,可以通过反射创建实例:
string postType = reader["Type"].ToString()!; string? contentJson = reader["Content"] != DBNull.Value ? reader["Content"].ToString() : null; if (_contentTypeMap.TryGetValue(postType, out var targetType)) { // 构建泛型Post<T>类型 Type genericPostType = typeof(Post<>).MakeGenericType(targetType); // 实例化泛型对象 dynamic post = Activator.CreateInstance(genericPostType)!; // 赋值Type字段 post.Type = postType; // 反序列化Content并赋值 if (!string.IsNullOrEmpty(contentJson)) { post.Content = JsonSerializer.Deserialize(contentJson, targetType)!; } // 其他字段可以通过反射赋值,或者强转为BaseEntity处理 // 例如:((BaseEntity)post).Id = reader.GetInt64(reader.GetOrdinal("Id")); }
优化建议
- 类型映射可以移到配置文件(如appsettings.json),方便后续新增类型时无需修改代码
- 增加Type字段的合法性校验,比如映射字典中不存在对应值时抛出异常或做降级处理
- 如果使用EF Core,可以自定义
ValueConverter实现自动映射,无需手动处理DataReader
内容的提问来源于stack exchange,提问作者Jonathan Anctil
相关产品推荐
相关产品推荐

