C# 当源DataTable行具有DBNull.Value时,SqlBulkCopy复制到默认列值失败的表中

C# 当源DataTable行具有DBNull.Value时,SqlBulkCopy复制到默认列值失败的表中,c#,sql-server,null,sqlbulkcopy,default-constraint,C#,Sql Server,Null,Sqlbulkcopy,Default Constraint,更新: 我有一个表定义为: CREATE TABLE [dbo].[csvrf_References] ( [Ident] [int] IDENTITY(1,1) NOT NULL, [ReferenceID] [uniqueidentifier] NOT NULL DEFAULT (newsequentialid()), [Type] [nvarchar](255) NOT NULL, [Location] [nvarchar](1000) NULL,

更新:

我有一个表定义为:

CREATE TABLE [dbo].[csvrf_References]
(
    [Ident] [int] IDENTITY(1,1) NOT NULL,
    [ReferenceID] [uniqueidentifier] NOT NULL DEFAULT (newsequentialid()),
    [Type] [nvarchar](255) NOT NULL,
    [Location] [nvarchar](1000) NULL,
    [Description] [nvarchar](2000) NULL,
    [CreatedOn] [datetime] NOT NULL DEFAULT (getdate()),
    [LastUpdatedOn] [datetime] NOT NULL DEFAULT (getdate()),
    [LastUpdatedUser] [nvarchar](100) NOT NULL DEFAULT (suser_sname()),

    CONSTRAINT [PK_References] PRIMARY KEY NONCLUSTERED ([ReferenceID] ASC)
) ON [PRIMARY]
我有一个
DataTable
,其中的列与表列名和数据类型匹配。
DataTable
CreatedOn
LastUpdatedOn
lastUpdateUser
中填写
DBNull.Value
<代码>引用ID已生成。当我调用下面的代码时,我得到下面的错误

代码:

错误:

尝试批量复制表csvrf_引用时出错
System.InvalidOperationException:列“CreatedOn”不允许DBNull.Value。
位于System.Data.SqlClient.SqlBulkCopy.ConvertValue(对象值,_SqlMetaData元数据,布尔值为空,布尔值和isSqlType,布尔值和强制数据源)


我已经找遍了,似乎找不到答案。
SqlBulkCopy
类似乎不遵守默认值,尽管它说它遵守默认值。我在这里做错了什么?

阅读有关
SqlBulkCopy
的文档,尤其是,我会得出与您相同的结论:SQL Server应该足够“智能”,在适用的情况下使用默认约束,特别是因为您没有使用
SqlBulkCopyOptions.KeepNulls
属性

然而,在这种情况下,我怀疑文件是微妙的错误;如果不是错误的话,那肯定是误导

正如您所观察到的,对于带有默认约束的不可为null的字段(在本例中为
GetDate()
),SqlBulkCopy会因上述错误而失败

作为测试,尝试创建模仿第一个表的第二个表,但这次将
CreatedOn
LastUpdatedOn
字段设置为空。在我的测试中,使用默认选项(
SqlBulkCopyOptions.default
)过程可以正常工作
CreatedOn
LastUpdatedOn
都在表中填充了正确的日期时间值,尽管这些字段的数据表值是
DBNull.value

作为另一个测试,使用相同的(可为空的字段)表,仅在这次使用
SqlBulkCopyOptions.KeepNulls
属性执行SqlBulkCopy。我想您会看到与我相同的结果,即,
CreatedOn
LastUpdatedOn
在表中都为空

此行为类似于执行“普通”T-SQL语句将数据插入表中

以原始表(不可为空的字段)为例,如果执行

INSERT INTO csvrf_References ([Type], [Location], [Description], [CreatedOn], [LastUpdatedOn], [LastUpdatedUser]) 
VALUES ('test', 'test', 'test', null, null, null)
您将收到一个关于表中不允许空值的类似错误

但是,如果从语句中省略不可为空的字段,SQL Server将使用这些字段的默认约束:

INSERT INTO csvrf_References ([Type], [Location], [Description]
VALUES ('test', 'test', 'still testing')
基于此,我建议要么将表中的字段设置为null(我认为这不是一个很好的选择),要么将“暂存”表用于SqlBulkCopy进程(其中字段可以设置为null,并且具有类似的默认约束)。一旦数据位于暂存表中,执行第二条语句将数据移动到实际的最终目标表中。

对于第1部分“默认值为非空的字段”,您不应该首先发送字段。它不应该被映射。无需更改该字段以仅为此接受null

对于第2部分,“使用默认值为NULL的字段”,只要您没有将SqlBulkCopyOptions设置为
KeepNulls
,它将在传入DbNull.value时获取默认值,否则它将插入实际的数据库
NULL

由于对
KeepNulls
的SqlBulkCopyOption有一些混淆,让我们看看它的定义:

在目标表中保留空值,而不考虑默认值的设置。如果未指定,空值将替换为默认值(如果适用)

这意味着,如果指定了
KeepNulls
选项,则即使列具有默认约束,设置为
DbNull.Value
的数据列也将作为数据库
NULL
插入。您的代码中没有指定它。这导致第二部分说
DbNull.Value
值被替换为“默认值”(如果适用)。此处“适用”表示列上定义了默认约束。因此,当存在默认约束时,将按原样发送非
DbNull.Value
值,而
DbNull.Value
应转换为SQL关键字
DEFAULT
。该关键字在INSERT语句中被解释为采用默认约束的值。当然,
SqlBulkCopy
,如果发出单独的INSERT语句,也有可能在该行设置为NULL时将该字段从列列表中删除,这将获取默认值。无论是哪种情况,最终结果都是它能按您的预期工作。我的测试表明它确实以这种方式工作

明确区别:

  • 如果数据库中的某个字段设置为
    非空
    ,并定义了默认约束,则选项为:

    • 在字段中传递(即,它不会拾取默认值),在这种情况下,它永远不能设置为
      DbNull.value

    • 完全不要传入字段(即,它将拾取默认值),这可以通过以下任一方式完成:

      • 在DataTable、query、DataReader或任何作为源发送的内容中都不要包含它,在这种情况下,您可能根本不需要指定
        ColumnMappings
        集合

      • 如果字段位于源中,则必须指定
        ColumnMappings
        集合,以便将该字段从映射中删除

    • 塞蒂
      INSERT INTO csvrf_References ([Type], [Location], [Description]
      VALUES ('test', 'test', 'still testing')