如何在具有GUID主键和默认newsequentialid()的表上使用SQLBulkCopy?

时间:2008-09-26 08:30:19

标签: .net sql

在具有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字段设置为允许空值并删除主键。

任何人都知道此问题是否有解决方法? 或者您可以验证没有解决方法(除了更改表结构)?

3 个答案:

答案 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方法中,但我不是那个写了大部分内容的人,而且我还没有通过它来调整所有内容。