Sua*_*ere 10 c# sql-server datatable
我在.NET 4.5.2中使用c#,推送到SQL Server 2017 14.0.1000.169
在我的数据库中,我有一个带有DateAdded字段的表,类型DateTimeOffset.
我正在尝试使用以下代码进行BulkCopy:
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();
}
Run Code Online (Sandbox Code Playgroud)
我知道does not allow DBNull错误的两个可能原因是:
DBNull.Value(或null?)在DataTable中设置.但我正确映射,并没有设置KeepNulls.
然而,我收到错误:
列DateAdded不允许DBNull.Value
编辑我也尝试了不设任何东西,包括null,DBNull.Value和DefaultValue...只是字面上没有设置该列在所有.
此外,如果我从DataTable中删除DateAdded列,它将工作.但我不希望这样.在100,000条记录中,可能有20条记录有数据.因此,在我的500个批次中,有时None在DateAdded字段中有数据,有时一个或两个有数据.
所以我想将列保留在我的DataTable中,但让它使用DefaultValue.
最后一点:我在将DataColumn的值设置为DBNull.Value与之间进行了交替dt.Columns[x.ColumnName].DefaultValue.两种方式都会产生相同的错误.
编辑2
这是我用来填充数据表中的数据的代码:
foreach (var column in table)
{
System.Data.DataRow newRow = dt.NewRow();
foreach (var field in column)
{
if (!IsNull(field.Value) && !IsEmptyDateOrNumber(field.ColumnType, field.Value))
{
// For DateAdded, this is not hit on the first batch, though there are columns Before and After DateAdded on the same row which do have value.
// But it WILL be hit once or twice a few batches later. So I don't want to completely remove the definition from the DataTable.
newRow[field.ColumnName] = field.Value;
}
else
{
// newRow[field.ColumnName] = dt.Columns[field.ColumnName].DefaultValue;
// newRow[field.ColumnName] = DBNull.Value;
// dt.Columns[field.ColumnName].AllowDBNull = true;
}
}
dt.Rows.Add(newRow);
}
Run Code Online (Sandbox Code Playgroud)
IsNull()TRUE如果值为null或字符串"null",则返回,这是我的业务要求所必需的.
IsEmptyDateOrNumber()TRUE如果字段是数字或日期类型,则返回,并且值为null或空"".因为虽然empty对于许多类似字符串的字段有效,但它永远不是有效的数值.
为该字段分配值的条件恰好是该特定列的0%.因此没有设置.
小智 5
简单地说,你不能做你想做的。关于 BulkCopy 如何使用默认值的最佳参考是 Rutzky 的这个答案。
问题是,BulkCopy 包括一个步骤,它查询目标数据库并确定表的结构。如果它确定目标列NOT NULL能够,并且您正在传递 null 或 DBNull,它甚至会在尝试传递数据之前抛出异常。
如果您使用 SQL Profiler,您将看到 BCP 调用,但看不到数据(数据永远不会显示)。您将看到的只是定义列列表和标志的调用。
当 BulkCopy 最终决定传递数据时。如果该列存在,并且该字段是NULL能够,并且值为DBNull.Value,并且该列有一个默认值;批量复制实质上是传递该DEFAULT列的标志。但是做出了一些决定,使得这些条件,除非该字段NOT NULL能够,否则不应使用默认值,而应抛出异常。
据我所知,这是微软的一个错误或疏忽。
正如其他一些答案所述,常见的解决方法是通过计算代码中应该包含的值来手动处理这些值。当然,如果您计算默认值,那么 DBA 会更改字段的实际 SQL 默认值,您的系统将不匹配。下一步是向您的系统添加一个子系统,该子系统从您正在访问的 SQL Server 查询和/或跟踪/缓存当前指定的默认值,并分配这些值。这比应该需要的工作要多得多。
TLDR:你不能做你想做的。但是其他人已经指定了一些次优的解决方法。