在具有GUID主键和默认newsequentialid()
的表上使用SQLBulkCopy时e.g
CREATE TABLE [dbo].[MyTable](
[MyPrimaryKey] [uniqueidentifier] NOT NULL CONSTRAINT [MyConstraint] DEFAULT (newsequentialid()),
[Status] [int] NULL,
[Priority] [int] NULL,
CONSTRAINT [PK_MyTable] PRIMARY KEY NONCLUSTERED
(
[MyPrimaryKey] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
与C#代码
tran = connection.BeginTransaction();
SqlBulkCopy sqlCopy = new SqlBulkCopy(connection,SqlBulkCopyOptions.Default, tran);
sqlCopy.DestinationTableName = "MyTable";
sqlCopy.WriteToServer(dataTable);
给你一个错误......
列'MyPrimaryKey'不允许DBNull.Value
我试过摆弄SqlBulkCopyOptions。唯一有效的方法是将MyPrimaryKey字段设置为允许空值并删除主键。
任何人都知道此问题是否有解决方法? 或者您可以验证没有解决方法(除了更改表结构)?
答案 0 :(得分:11)
您需要设置列映射。第一次打电话
sqlCopy.ColumnMappings.Clear();
然后致电
sqlBulkCopy.ColumnMappings.Add("Status", "Status");
sqlBulkCopy.ColumnMappings.Add("Priority", "Priority");
这意味着批量副本将停止尝试插入MyPrimaryKey列,并且只会插入状态列和优先级列。
答案 1 :(得分:0)
您只有选择从正在加载的数据中删除MyPrimaryKey字段或修改表结构。
如果字段在那里没有值,那么你告诉SQL你要强制在字段中使用null,这显然是不允许的。
答案 2 :(得分:0)
在写入之前从列集中删除数据库生成的列是您需要做的。
我们在大多数数据库操作中使用LINQ-to-SQL,但是使用另一种方法一次插入多个记录,因为L2S对此有点慢。
我们有一个名为BulkInsertAll<>
的通用方法,我们可以在任何内部使用SqlBulkCopy
的表上使用它。我们使用基于泛型类型属性的反射动态生成列。 {。{1}}位于.dbml文件生成的.cs文件中,我们已将guid主键列指定为ColumnAttribute
。
IsDbGenerated="true"
这很好用。实体不会使用新分配的Guid进行更新,因此您必须再次进行查询以获取这些实体,但新行在数据库中具有属性生成的guid。
我们可以将public void BulkInsertAll<T>( IEnumerable<T> entities ) {
entities = entities.ToArray();
string cs = Connection.ConnectionString;
var conn = new SqlConnection( cs );
conn.Open();
Type t = typeof( T );
var tableAttribute = (TableAttribute) t.GetCustomAttributes(
typeof( TableAttribute ), false
).Single();
var bulkCopy = new SqlBulkCopy( conn ) {
DestinationTableName = tableAttribute.Name
};
var properties = t.GetProperties().Where( EventTypeFilter );
// This will prevent the bulk insert from attempting to update DBGenerated columns
// Without, inserts with a guid pk will fail to get the generated sequential id
// If uninitialized guids are passed to the DB, it will throw duplicate key exceptions
properties = properties.Where(
x => !x.GetCustomAttributes( typeof( ColumnAttribute ), false )
.Cast<ColumnAttribute>().Any( attr => attr.IsDbGenerated )
);
var table = new DataTable();
foreach( var property in properties ) {
Type propertyType = property.PropertyType;
if( propertyType.IsGenericType &&
propertyType.GetGenericTypeDefinition() == typeof( Nullable<> ) ) {
propertyType = Nullable.GetUnderlyingType( propertyType );
}
table.Columns.Add( new DataColumn( property.Name, propertyType ) );
}
foreach( var entity in entities ) {
table.Rows.Add(
properties.Select(
property => GetPropertyValue( property.GetValue( entity, null ) )
).ToArray()
);
}
//specify the mapping for SqlBulk Upload
foreach( var col in properties ) {
bulkCopy.ColumnMappings.Add( col.Name, col.Name );
}
bulkCopy.WriteToServer( table );
conn.Close();
}
private bool EventTypeFilter( System.Reflection.PropertyInfo p ) {
var attribute = Attribute.GetCustomAttribute( p,
typeof( AssociationAttribute ) ) as AssociationAttribute;
if( attribute == null ) return true;
if( attribute.IsForeignKey == false ) return true;
return false;
}
private object GetPropertyValue( object o ) {
if( o == null )
return DBNull.Value;
return o;
}
过滤器包装到EventTypeFilter方法中,但我不是那个写了大部分内容的人,而且我还没有通过它来调整所有内容。