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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 22:37:47