2008-09-26 8 views
6

एक मेज पर SqlBulkCopy का उपयोग करते समय एक GUID प्राथमिक कुंजी और डिफ़ॉल्ट newsequentialid()एक GUID प्राथमिक कुंजी और डिफ़ॉल्ट newsequentialid() के साथ एक तालिका पर SQLBulkCopy का उपयोग कैसे कर सकते हैं?

जैसे

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] 

सी # कोड

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

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

आपको एक त्रुटि देता है साथ ...

कॉलम 'MyPrimaryKey' DBNull की अनुमति नहीं देता है। वैल्यू

मैंने SqlBulkCopyOptions को झुकाव करने का प्रयास किया है। काम करने वाली एकमात्र चीज MyPrimaryKey फ़ील्ड को नल को अनुमति देने और प्राथमिक कुंजी को हटाने के लिए सेट कर रही है।

किसी को भी पता है कि इस मुद्दे के लिए कोई समाधान है या नहीं? या क्या आप सत्यापित कर सकते हैं कि कोई वर्कअराउंड नहीं है (तालिका संरचना को बदलने के अलावा)?

उत्तर

0

आप केवल विकल्प हैं जिन्हें लोड किया जा रहा डेटा या तालिका संरचना को संशोधित करने के लिए MyPrimaryKey फ़ील्ड को निकालना है।

फ़ील्ड के साथ कोई मूल्य नहीं होने के साथ आप SQL को बता रहे हैं कि आप फ़ील्ड में एक शून्य को मजबूर करना चाहते हैं, जो स्पष्ट रूप से अनुमति नहीं है।

11

आपको कॉलम मैपिंग सेट अप करने की आवश्यकता है। सबसे पहले कॉल

sqlCopy.ColumnMappings.Clear(); 

फिर

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

फोन इसका मतलब यह है थोक प्रतिलिपि MyPrimaryKey स्तंभ में डालने की कोशिश कर बंद हो जाएगा और स्थिति और प्राथमिकता कॉलम में केवल डाल देंगे।

+0

बहुत बढ़िया, यह मुझे मदद की !!!!! – HaBo

+0

JRummell, क्या GUID कॉलम स्वचालित रूप से SQLBULKCOPY आदेश के माध्यम से डाली गई प्रत्येक पंक्ति के लिए एक नया मान उत्पन्न करेगा? अग्रिम में धन्यवाद। –

0

लिखने से पहले कॉलम सेट से डेटाबेस जेनरेट किए गए कॉलम को हटाने से आपको क्या करना है।

हम अपने डेटाबेस कार्यों के अधिकांश के लिए LINQ करने वाली एसक्यूएल उपयोग करें, लेकिन, एक ही बार में कई रिकॉर्ड डालने के लिए एक और विधि का उपयोग के रूप में L2S इस के लिए थोड़ा धीमा है।

हमारे पास BulkInsertAll<> नामक एक सामान्य विधि है जिसे हम किसी भी तालिका पर उपयोग कर सकते हैं, जो आंतरिक रूप से SqlBulkCopy का उपयोग करता है। हम जेनेरिक प्रकार के गुणों के आधार पर प्रतिबिंब का उपयोग करके गतिशील रूप से कॉलम उत्पन्न करते हैं। ColumnAttribute .cs हमारे .dbml फ़ाइल, जहां हम IsDbGenerated="true" के रूप में 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; 
} 

और यह ठीक काम करता है। संस्थाओं को नए असाइन किए गए ग्रिड के साथ अपडेट नहीं किया जाएगा, इसलिए आपको उनको प्राप्त करने के लिए एक और प्रश्न बनाना होगा, लेकिन नई पंक्तियों में डेटाबेस में संपत्ति उत्पन्न ग्रिड हैं।

हम उस .Where को इवेंट टाइप टाइपर विधि में फ़िल्टर कर सकते हैं, लेकिन मैं वह नहीं हूं जिसने इसे सबसे अधिक लिखा है, और मैं इसे सब कुछ ट्यून करने के लिए नहीं चला हूं।

संबंधित मुद्दे