2008-09-26 18 views
6

Khi sử dụng SqlBulkCopy trên một bảng với một khóa chính GUID và newsequentialid mặc định()Làm thế nào có thể sử dụng SQLBulkCopy trên một bảng với một khóa chính GUID và mặc định newsequentialid()?

ví dụ

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] 

với mã C#

 tran = connection.BeginTransaction(); 
     SqlBulkCopy sqlCopy = new SqlBulkCopy(connection,SqlBulkCopyOptions.Default, tran);    

     sqlCopy.DestinationTableName = "MyTable";    
     sqlCopy.WriteToServer(dataTable); 

Cung cấp cho bạn một lỗi ...

Cột 'MyPrimaryKey' không cho phép DBNull.Value

Tôi đã thử sử dụng SqlBulkCopyOptions. Điều duy nhất hoạt động là thiết lập trường MyPrimaryKey để cho phép null và loại bỏ khóa chính.

Bất kỳ ai biết liệu có giải pháp cho sự cố này không? Hoặc bạn có thể xác minh rằng không có cách giải quyết nào khác (ngoài việc thay đổi cấu trúc bảng)?

Trả lời

0

Bạn chỉ có các tùy chọn để xóa trường MyPrimaryKey khỏi dữ liệu đang được tải hoặc sửa đổi cấu trúc bảng.

Với trường đang ở đó mà không có giá trị nào bạn đang nói với SQL rằng bạn muốn buộc một giá trị rỗng vào trường, mà rõ ràng là không được phép.

11

Bạn cần thiết lập ánh xạ cột. Đầu tiên gọi

sqlCopy.ColumnMappings.Clear(); 

Sau đó gọi

sqlBulkCopy.ColumnMappings.Add("Status", "Status"); 
sqlBulkCopy.ColumnMappings.Add("Priority", "Priority"); 

này có nghĩa là sao chép số lượng lớn sẽ ngừng cố gắng để chèn vào cột MyPrimaryKey và sẽ chỉ chèn vào các cột trạng và ưu tiên.

+0

Tuyệt vời, điều này đã giúp tôi !!!!! – HaBo

+0

JRummell, cột GUID có tự động tạo ra một giá trị mới cho mỗi hàng được chèn qua lệnh SQLBULKCOPY không? Cảm ơn trước. –

0

Xóa cột được tạo cơ sở dữ liệu khỏi tập hợp cột trước khi viết là những gì bạn cần làm.

Chúng tôi sử dụng LINQ-to-SQL cho hầu hết các hoạt động cơ sở dữ liệu của chúng tôi, nhưng sử dụng một phương pháp khác để chèn nhiều bản ghi cùng một lúc, vì L2S hơi chậm cho việc này.

Chúng tôi có phương pháp chung được gọi là BulkInsertAll<> mà chúng tôi có thể sử dụng trên bất kỳ bảng nào sử dụng SqlBulkCopy nội bộ. Chúng ta tạo động các cột bằng cách sử dụng sự phản chiếu dựa trên các thuộc tính của kiểu generic. Đã tìm thấy ColumnAttribute trong tệp .cs được tạo từ tệp .dbml của chúng tôi, trong đó chúng tôi đã chỉ định cột khóa chính guid là IsDbGenerated="true".

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; 
} 

Và điều này chỉ hoạt động tốt. Các thực thể sẽ không được cập nhật với Guid mới được chỉ định, vì vậy bạn sẽ phải thực hiện một truy vấn khác để nhận được các truy vấn đó, nhưng các hàng mới có các thuộc tính được tạo thuộc tính trong cơ sở dữ liệu.

Chúng tôi có thể bọc bộ lọc .Where vào phương pháp EventTypeFilter, nhưng tôi không phải là người đã viết hầu hết điều này và tôi chưa trải qua nó để điều chỉnh mọi thứ.

Các vấn đề liên quan