使用 SqlBulkCopy 将 DEFAULT 关键字传递给不可为空的列

Pass DEFAULT keyword to non-nullable column with SqlBulkCopy

有没有办法将 DEFAULT 关键字从 IDataReader 传递给 SqlBulkCopy?
基本上相当于

INSERT INTO MyTable(NonNullableWithDefault)
VALUES (DEFAULT)

在我的应用程序中,目标 table 具有不可为 null 的列,这些列具有默认约束。
有些要上传的数据具有这些列的值,有些则没有。
对于没有值(DBNull)的行,它应该使用默认约束。

this answer 中所述,SqlBulkCopy 要么希望您根本不传入 ColumnMapping,要么发送一个值。

我正在考虑的备选方案

我不是第一个遇到这个问题的人。
人们在这种情况下使用什么?

更新

经过多次摆弄后,我现在用一个查询获取我的整个数据库结构,然后将该数据集传递给我的数据读取器。

SELECT
    columns.TABLE_SCHEMA                            schemaName
  , columns.TABLE_NAME                              tableName
  , columns.TABLE_SCHEMA + '.' + columns.TABLE_NAME fullTableName
  , columns.COLUMN_NAME                             columnName
  , columns.DATA_TYPE                               dataType
  , ISNULL(columns.CHARACTER_MAXIMUM_LENGTH, -1)    charlength
  , columns.COLUMN_DEFAULT                          defaultValue
  , columns.ORDINAL_POSITION                        ordinalPosition
FROM
  information_schema.columns columns

默认值字符串的解析不完美,很可能不适合高级情况。然而,在我们的案例中,它涵盖了所有基础。

private object ParseDefault(DataRow row)
{
    if(row.IsNull("defaultValue")) return null;

    var value = row.Field<string>("defaultValue");
    if (value == "(getdate())")
    {
        return DateTime.UtcNow;
    }
    var type = GetTypeForName(row.Field<string>("dataType"));
    return ParseCell(value.Trim('(', ')'), type);
}

private static object ParseCell(string value, Type info)
{
    if (value.Equals("NULL", StringComparison.OrdinalIgnoreCase))
    {
        return DBNull.Value;
    }

    int intValue;
    if (info == typeof(bool) && int.TryParse(value, out intValue))
    {
        return intValue == 1;
    }
    var foo = TypeDescriptor.GetConverter(info);
    return foo.ConvertFromInvariantString(value);
}

优点:

缺点

Is there a way to pass the DEFAULT keyword to SqlBulkCopy from an IDataReader?

很遗憾,没有。 :-( 如果列是 NOT NULL 并且您的批量插入映射到它,则每一行都必须具有该列的值。

背景:在 TDS 级别,通过发送包含列元数据然后是行数据的 SQL INSERT statement written using the external tools only syntax followed by TDS structures 来完成批量插入。 None 其中提供了一种表达方式 "if a NULL value is in a non-NULLable column, treat the NULL as a DEFAULT."

What do people use in such a scenario?

嗯...当感觉应该有内置的、简单的解决方案而不是所有选项都令人痛苦时,这令人沮丧。 :-/ 在不了解您的情况的情况下,很难知道推荐哪个选项。

一个附加选项:占位符值方法。在插入之前,将“NULLs that should be defaulted”替换为占位符值。然后,post-插入,运行 更新以用数据库生成的默认值(例如 UPDATE ... SET Col1 = DEFAULT WHERE Col1 = *placeholder*)替换占位符。您的应用程序必须知道哪些不可​​为 null 的列具有默认值,但不必知道如何计算默认值。我不关心它,因为它使用所谓的幻数——但它是一个选项。