C# 列不允许DBNull.Value-无KeepNulls-正确的列映射
我正在使用c#with.NET 4.5.2,并将其推到SQL Server 2017 14.0.1000.169 在我的数据库中,我有一个带有DateAdded字段的表,类型为C# 列不允许DBNull.Value-无KeepNulls-正确的列映射,c#,sql-server,datatable,C#,Sql Server,Datatable,我正在使用c#with.NET 4.5.2,并将其推到SQL Server 2017 14.0.1000.169 在我的数据库中,我有一个带有DateAdded字段的表,类型为DateTimeOffset 我正在尝试使用以下代码进行批量复制: private Maybe BulkCopy(SqlSchemaTable table, System.Data.DataTable dt, bool identityInsertOn) { try { var option
DateTimeOffset
我正在尝试使用以下代码进行批量复制:
private Maybe BulkCopy(SqlSchemaTable table, System.Data.DataTable dt, bool identityInsertOn)
{
try
{
var options = SqlBulkCopyOptions.TableLock | SqlBulkCopyOptions.FireTriggers | SqlBulkCopyOptions.UseInternalTransaction; // | SqlBulkCopyOptions.CheckConstraints; // Tried CheckConstraints, but it didn't change anything.
if (identityInsertOn) options |= SqlBulkCopyOptions.KeepIdentity;
using (var conn = new SqlConnection(_connString))
using (var bulkCopy = new SqlBulkCopy(conn, options, null))
{
bulkCopy.DestinationTableName = table.TableName;
dt.Columns.Cast<System.Data.DataColumn>().ToList()
.ForEach(x => bulkCopy.ColumnMappings.Add(new SqlBulkCopyColumnMapping(x.ColumnName, x.ColumnName)));
try
{
conn.Open();
bulkCopy.WriteToServer(dt);
}
catch (Exception ex)
{
return Maybe.Failure(ex);
}
}
}
catch (Exception ex)
{
return Maybe.Failure(ex);
}
return Maybe.Success();
}
IsNull()
如果值为null
或字符串为“null”
,则返回TRUE
,这是我的业务需求所必需的
IsEmptyDateOrNumber()
将返回TRUE
如果字段是数字或日期类型,并且值为null或空“”
。因为虽然empty对许多类似字符串的字段有效,但它永远不是有效的数值
指定字段值的条件正好命中此特定列时间的0%。因此未设置任何内容。此组合不适用于SqlBulkCopy:
CREATE TABLE [dbo].[MyTable](
[Field1] [varchar](50) NULL,
[MyDateField] [datetime] NULL,
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[MyTable] ADD CONSTRAINT [DF_Table_1_MyDateField] DEFAULT (getdate()) FOR [MyDateField]
GO
然后在C#中:
在100000条记录中,可能有20条有数据。所以在我的批次中
在500个字段中,有时在DateAdded字段中没有数据,有时是一个
一两个有数据
我猜您的一些记录在DateAdded中有空值,而DateAdded被配置为不接受空值
要克服此问题,您有多种方法:
if(field.ColumnName == "DateAdded")
{
// Do something to handle this column if it's null
// Set a default value
DateTimeOffset dtoffset = DateTimeOffset.Parse("2019-01-01 00:00:00.0000000 -00:00", CultureInfo.InvariantCulture); // change it to the required offset
string defaultDateAdded = dtoffset.ToString("yyyy-MM-dd HH:mm:ss.fffffff zzz", CultureInfo.InvariantCulture);
// Add the default value
newRow[field.ColumnName] = defaultDateAdded;
}
else
{
// Do something to handle the rest of columns if they're null
}
然后,当处理完所有列后,只需将新行添加到数据表中,然后将最终确定的数据表复制到服务器。简单地说,你不能做你想做的事。关于BulkCopy如何使用默认值的最佳参考是Rutzky 问题是,BulkCopy包括一个步骤,它在该步骤中查询目标数据库并确定表的结构。如果它确定目标列是
notnull
able,并且您正在传递NULL或DBNull,那么它会在尝试传递数据之前抛出异常
如果使用SQL Profiler,您将看到BCP调用,但不会看到数据(数据无论如何都不会显示)。您将看到的只是定义列列表和标志的调用
当BulkCopy最终决定传递数据时。如果该列存在,字段为NULL
able,值为DBNull.value,该列有默认值;大容量复制基本上会为该列传递DEFAULT
标志。但是做出了一些决定,除了字段为notnull
able之外,这些条件不应该使用默认值,而是应该抛出一个异常
据我所知,这是微软的错误或疏忽
与其他一些答案一样,常见的解决方法是通过计算代码中的值来手动处理这些值。当然,如果您计算默认值,那么DBA会更改字段的实际SQL默认值,您的系统将不匹配。下一步是向系统中添加子系统,该子系统查询和/或跟踪/缓存正在访问的SQL Server中当前指定的默认值,并分配这些值。这是比应该需要做的更多的工作
TLDR:你不能做你想做的事。但也有其他人指定的次优解决方法。很可能DB中添加的DateAdd列被设置为不允许为null,并且几乎总是默认为getdate()。如果默认值设置正确,则不要尝试插入该列。@KeithL谢谢。这就是托尼的答案。我们试过了,但仍然有错误。我更新了我的问题以表明我们已经尝试过了。我仍然认为该表不允许在该列中插入null。换句话说,它需要一个值。是否尝试设置CheckConstraints SQLBulkCopy选项?如果排除该列,SQLBulkCopy似乎只能正确处理此问题。i、 e.此列的所有行都有一个默认值。这是SQLBulkCopy的源代码,我以前将其标记为答案,但现在已取消标记。我所做的是完全跳过该列的赋值,尽管该列仍然存在于DataTable定义中。这似乎是可行的,但事实证明,我并没有删除之前的测试代码,而这些代码实际上是在从DataTable定义中删除列。因此,在解决问题时,这是一个假阳性。我会花一些时间来研究FireTriggers,如果这有助于解决问题,我会重新应用它作为答案
DataTable dt = new DataTable("dbo.MyTable");
dt.Columns.Add("Field1");
dt.Columns.Add("MyDateField");
DataRow row1 = dt.NewRow();
row1["Field1"] = "test row 1";
row1["MyDateField"] = DateTime.Now; //specify a value for the date field in C#
dt.Rows.Add(row1);
DataRow row2 = dt.NewRow();
row2["Field1"] = "test row 2";
//do not specify a value for the date field - SQL will use the default value
dt.Rows.Add(row2);
do the bulk copy
if(field.ColumnName == "DateAdded")
{
// Do something to handle this column if it's null
// Set a default value
DateTimeOffset dtoffset = DateTimeOffset.Parse("2019-01-01 00:00:00.0000000 -00:00", CultureInfo.InvariantCulture); // change it to the required offset
string defaultDateAdded = dtoffset.ToString("yyyy-MM-dd HH:mm:ss.fffffff zzz", CultureInfo.InvariantCulture);
// Add the default value
newRow[field.ColumnName] = defaultDateAdded;
}
else
{
// Do something to handle the rest of columns if they're null
}