使用Dapper动态处理非空且默认值为NULL的数据库列插入需求
替代方案汇总
方案1:基于实体类默认值+Dapper动态参数合并
如果你的C#实体类已与数据库表结构一一对应,可直接在实体类的非空无默认值字段上设置代码层面的默认值,再将用户反序列化的对象与默认值实体合并,最后用Dapper执行插入。
示例代码:
// 定义实体类,给必填字段设置默认值 public class ExampleTable { public string SentByUser1 { get; set; } public string SentByUser2 { get; set; } public string SentByUser3 { get; set; } // 非空无默认值的字段,设置代码默认值 public string NotSentByUser1 { get; set; } = "value_decided_in_code1"; public DateTime NotSentByUser2 { get; set; } = DateTime.Now; } // 处理用户请求 var userInputJson = "..."; // 用户发送的JSON var userData = JsonSerializer.Deserialize<ExampleTable>(userInputJson); // 创建默认实体,合并用户数据(仅覆盖用户提供的属性) var defaultEntity = new ExampleTable(); var mergedData = new ExampleTable { SentByUser1 = userData.SentByUser1 ?? defaultEntity.SentByUser1, SentByUser2 = userData.SentByUser2 ?? defaultEntity.SentByUser2, SentByUser3 = userData.SentByUser3 ?? defaultEntity.SentByUser3, NotSentByUser1 = userData.NotSentByUser1 ?? defaultEntity.NotSentByUser1, NotSentByUser2 = userData.NotSentByUser2 != default ? userData.NotSentByUser2 : defaultEntity.NotSentByUser2 }; // 用Dapper执行插入 using var connection = new SqlConnection(connectionString); connection.Execute(@" INSERT INTO example_table (sent_by_user1, sent_by_user2, sent_by_user3, NOT_sent_by_user1, NOT_sent_by_user2) VALUES (@SentByUser1, @SentByUser2, @SentByUser3, @NotSentByUser1, @NotSentByUser2)", mergedData);
优点:无需查询数据库元数据,代码简洁;缺点:需保证实体类与表结构严格同步,若表结构变更,需手动更新实体类默认值。
方案2:利用EF Core模型元数据(若项目已引入EF Core)
如果你的项目已经使用EF Core,可直接从DbContext的实体模型中获取非空且无默认值的列信息,无需查询INFORMATION_SCHEMA。
示例代码:
// 假设已有DbContext public class AppDbContext : DbContext { public DbSet<ExampleTable> ExampleTables { get; set; } // ... 其他配置 } // 获取目标表的必填列(非空且无默认值) using var dbContext = new AppDbContext(); var entityType = dbContext.Model.FindEntityType(typeof(ExampleTable)); var requiredColumns = entityType.GetProperties() .Where(p => p.IsNullable == false && p.GetDefaultValue() == null) .Select(p => p.GetColumnName()) // 获取数据库列名 .ToList(); // 合并用户数据与默认值 var userData = JsonSerializer.Deserialize<ExampleTable>(userInputJson); var parameters = new DynamicParameters(userData); // 为必填列设置默认值(根据类型分配) foreach (var column in requiredColumns) { var property = entityType.GetProperties().First(p => p.GetColumnName() == column); var defaultValue = property.ClrType switch { Type t when t == typeof(string) => "default_value", Type t when t == typeof(int) => 0, Type t when t == typeof(DateTime) => DateTime.Now, // 其他类型按需添加 _ => throw new NotSupportedException($"Unsupported type {property.ClrType}") }; // 仅当用户未提供该值时设置默认值 if (!parameters.ParameterNames.Contains(column)) { parameters.Add(column, defaultValue); } } // 动态构建INSERT语句 var columns = parameters.ParameterNames.ToList(); var columnNames = string.Join(", ", columns); var paramNames = string.Join(", ", columns.Select(c => $"@{c}")); var sql = $"INSERT INTO example_table ({columnNames}) VALUES ({paramNames})"; connection.Execute(sql, parameters);
优点:基于代码模型获取元数据,无需查询数据库;缺点:依赖EF Core,若项目未使用EF则需额外引入依赖。
方案3:T4模板预生成表结构元数据
可以使用T4模板定期从数据库生成包含必填列信息的静态类,程序运行时直接使用该类的信息,避免运行时查询数据库。
示例T4模板片段(生成静态类):
<#@ template language="C#" #> <#@ assembly name="System.Data" #> <#@ import namespace="System.Data.SqlClient" #> <# var connectionString = "你的数据库连接字符串"; var tableName = "example_table"; using var conn = new SqlConnection(connectionString); conn.Open(); var cmd = conn.CreateCommand(); cmd.CommandText = @" SELECT COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE IS_NULLABLE = 'NO' AND TABLE_NAME = @TableName AND COLUMN_DEFAULT IS NULL"; cmd.Parameters.AddWithValue("@TableName", tableName); var reader = cmd.ExecuteReader(); #> public static class ExampleTableMetadata { public static readonly Dictionary<string, Type> RequiredColumns = new Dictionary<string, Type> { <# while(reader.Read()) { var columnName = reader["COLUMN_NAME"].ToString(); var dataType = reader["DATA_TYPE"].ToString(); var clrType = dataType switch { "varchar" => typeof(string), "int" => typeof(int), "datetime" => typeof(DateTime), // 映射其他SQL类型到CLR类型 _ => typeof(object) }; #> { "<#= columnName #>", typeof(<#= clrType.Name #>) }, <# } #> }; }
生成后,运行时直接使用ExampleTableMetadata.RequiredColumns获取必填列,再按方案1的方式合并默认值、构建插入语句。
优点:运行时无数据库查询,性能高;缺点:需定期更新T4模板生成的代码以同步表结构变更。
内容的提问来源于stack exchange,提问作者Librapulpfiction
相关产品推荐
相关产品推荐

