C#如何抽取两段相似数据库读写转换逻辑为通用公共函数
实现方案
核心通过泛型+委托的组合实现代码复用,不需要为OldSteamSystem、OldFuelSystem提取公共接口或合并类定义,完全兼容两类差异较大的旧实体结构。
可变逻辑全部通过参数传入:
- 表名通过字符串参数传入,用于动态拼接查询、更新SQL
- 旧实体类型通过泛型参数指定,适配不同类型的反序列化需求
- 转换逻辑通过Func委托传入,公共方法不需要感知具体的实体转换规则
其余固定的数据库操作、DataReader读取、JSON序列化/反序列化、更新执行逻辑全部下沉到公共函数统一维护。
通用公共函数代码
private void ProcessTableInitialObjectMigration<TOldEntity>( DbContext dbContext, MigrationBuilder migrationBuilder, string tableName, Func<TOldEntity, object> convertDelegate) { using var queryCmd = dbContext.Database.GetDbConnection().CreateCommand(); queryCmd.CommandText = @$"SELECT ""Id""::varchar, ""InitialObject""::varchar FROM ""{tableName}"""; // 补全连接打开逻辑,避免连接未开启时执行报错 if (queryCmd.Connection.State != ConnectionState.Open) { queryCmd.Connection.Open(); } using var reader = queryCmd.ExecuteReader(); if (!reader.HasRows) { reader.Close(); return; } while (reader.Read()) { var recordId = reader.IsDBNull(0) ? Guid.Empty : Guid.Parse(reader.GetString(0)); var initialJson = reader.IsDBNull(1) ? "null" : reader.GetString(1); // 按传入的泛型类型反序列化旧实体 var oldEntityInstance = JsonConvert.DeserializeObject<TOldEntity>(initialJson); // 调用传入的转换委托完成结构映射 var convertedEntity = convertDelegate(oldEntityInstance); // 序列化后做单引号转义,兼容SQL语法 var updatedJsonStr = JsonConvert.SerializeObject(convertedEntity) .Replace("'", "''", StringComparison.Ordinal); migrationBuilder.Sql(@$"UPDATE ""{tableName}"" SET ""InitialObject"" = '{updatedJsonStr}'::jsonb WHERE ""Id"" = '{recordId}'"); } reader.Close(); }
调用方式
原有两段重复的业务逻辑,替换为以下两行调用即可:
// 处理蒸汽系统表 ProcessTableInitialObjectMigration<OldSteamSystem>( dbContext, migrationBuilder, "DesignHubProjectSteamSystems", TransformProjectSteamSystem); // 处理燃油系统表 ProcessTableInitialObjectMigration<OldFuelSystem>( dbContext, migrationBuilder, "DesignHubProjectFuelSystems", TransformProjectFuelSystem);
补充优化建议
原SQL拼接写法存在隐式注入风险,可将更新逻辑改为参数化执行,无需手动转义单引号,稳定性更强,调整公共函数内的更新段代码即可,上层调用无需修改。
内容的提问来源于stack exchange,提问作者Glory Raj
相关产品推荐
相关产品推荐

