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

从数据库行生成C#类的实现方案与优化问询

问题描述

我有若干C#类,需要将其成员以单独行的形式存储到数据库中,示例类结构如下:

public class Root
{
    public Prius Prius { get; set; }
}

public class Prius
{
    public int SteeringWheel { get; set; }
    public Wheels Wheels { get; set; }
    public Power Power { get; set; }
}

public class Wheels
{
    public int Front { get; set; }
    public int Rear { get; set; }
}

public class Power
{
    public Gasoline Gasoline { get; set; }
    public Battery Battery { get; set; }
}

public class Gasoline
{
    public int Capacity { get; set; }
}

public class Battery
{
    public int Size { get; set; }
}

硬性要求

  • 单个值需存储在数据库中,并附带额外列;
  • 键存在多个版本,查询时仅获取每个键的最新版本条目;
  • 仅执行读取操作,应用不向数据库写回数据。

我计划将数据存储为如下表结构:

KeyValueVersionMetadata col 1More columns...
Prius:SteeringWheel01test....
Prius:SteeringWheel12test....
Prius:Wheels:Front21test....
Prius:Wheels:Rear21test....
...remaining rows.............

我计划通过以下步骤从这些值生成C#对象:

  1. 使用EF Core一次性读取所有值;
  2. 生成对应的C#对象,之后通过构造函数参数传递给应用中的其他类。

现提出两个问题:

  1. 基于该方案,如何利用C#/.NET现有特性以最少代码实现?
  2. 该方案是否为最优选择?若否,请给出替代方案建议。

补充说明

  • 已修正重复的wheels属性;
  • 希望避免创建EF实体,以便未来增删属性时仅更新代码而无需修改数据库架构,且仅读取键的最新版本。

回答

问题1:最少代码实现方案

1. 读取最新版本数据

利用EF Core的无实体查询能力,直接通过SQL获取每个键的最新版本数据,无需注册EF实体:

// 自定义匿名类型接收查询结果(包含需要的字段)
var latestEntries = context.Database.SqlQuery<(string Key, string Value, string MetadataCol1)>(@"
    SELECT t.Key, t.Value, t.[Metadata col 1]
    FROM YourTableName t
    INNER JOIN (
        SELECT Key, MAX(Version) AS MaxVersion
        FROM YourTableName
        GROUP BY Key
    ) latest ON t.Key = latest.Key AND t.Version = latest.MaxVersion
").ToList();

2. 映射为C#对象

推荐用JSON中转的方式,依赖System.Text.Json的序列化能力,无需手写复杂反射逻辑:

// 第一步:将键值对转换为嵌套字典
var nestedDict = new Dictionary<string, object>();
foreach (var entry in latestEntries)
{
    var keySegments = entry.Key.Split(':');
    var currentLevel = nestedDict;
    
    // 逐层构建嵌套字典
    for (int i = 0; i < keySegments.Length - 1; i++)
    {
        if (!currentLevel.ContainsKey(keySegments[i]))
            currentLevel[keySegments[i]] = new Dictionary<string, object>();
        currentLevel = (Dictionary<string, object>)currentLevel[keySegments[i]];
    }
    
    // 转换值类型(匹配目标属性类型)
    var targetType = GetTargetPropertyType(typeof(Root), keySegments);
    currentLevel[keySegments.Last()] = Convert.ChangeType(entry.Value, targetType);
}

// 第二步:序列化为JSON后反序列化为目标对象
var json = JsonSerializer.Serialize(nestedDict);
var root = JsonSerializer.Deserialize<Root>(json);

// 辅助方法:递归获取目标属性的类型
private Type GetTargetPropertyType(Type currentType, string[] keySegments)
{
    foreach (var segment in keySegments)
    {
        var prop = currentType.GetProperty(segment);
        currentType = prop.PropertyType;
    }
    return currentType;
}

这种方式代码量极少,且利用.NET原生序列化能力,避免了手动反射的维护成本。


问题2:方案是否最优?替代建议

现有方案的优缺点

优点

  • 数据库架构完全灵活:增删C#类属性无需修改表结构,仅需调整代码;
  • 单属性版本追溯方便:可单独查看某个属性的历史版本。

缺点

  • 查询效率低:每次需聚合关联获取最新版本,数据量大时性能瓶颈明显;
  • 类型安全风险:Value存储为字符串,类型转换需额外处理错误;
  • 映射复杂度高:嵌套结构的键拆分、类型转换逻辑随类结构复杂度上升。

替代方案建议

方案1:单表存储完整JSON(带版本)

将整个Root对象序列化为JSON字符串,存储到带版本的表中:

IdVersionContentJsonMetadata col 1More columns...
11{"Prius":{"SteeringWheel":0,...}}test....
12{"Prius":{"SteeringWheel":1,...}}test....
优势
  • 映射代码极简:直接反序列化JSON为对象,无需处理嵌套键;
  • 查询更高效:按Id和最大Version查询即可,无需复杂关联;
  • 类型安全:序列化/反序列化过程自动处理类型校验。
适用场景
  • 无需单独查询单个属性值,仅需加载整个对象;
  • 类结构整体变动不频繁,或变动时无需单独追踪单个属性版本。

方案2:使用文档型数据库(如MongoDB)

如果允许切换数据库,文档型数据库天然适配嵌套结构:

  • 直接将Root对象作为文档存储,每个文档带版本号;
  • 查询时过滤最新版本文档,直接反序列化为C#对象。
优势
  • 完全匹配C#类的嵌套结构,无需手动拆分键值;
  • 原生支持复杂对象序列化,映射代码几乎为零;
  • 版本管理更灵活,可通过数据库自带的版本控制功能实现。

方案3:优化键值存储的查询性能

如果必须保留键值结构,可做以下优化:

  • 给(Key, Version)建立复合索引,提升最大版本查询的速度;
  • 用Dapper替代EF Core,无实体查询更轻量,代码更简洁:
var latestEntries = connection.Query<(string Key, string Value)>(@"
    SELECT t.Key, t.Value
    FROM YourTableName t
    WHERE t.Version = (
        SELECT MAX(Version) FROM YourTableName WHERE Key = t.Key
    )
").ToList();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:52:53