2012-12-13 84 views
2

如何在C#中将通用列表批量插入到SQL Server中,而不是循环遍历列表并逐个插入单个项目?将泛型列表C#插入到SQL Server中

我目前有这个;

private void AddSnapshotData() 
{ 
    var password =  Cryptography.DecryptString("vhx7Hv7hYD2bF9N4XhN5pkQm8MRfxi+kogALYqwqSuo="); 
    var figDb = "ZEUS"; 
    var connString = 
    String.Format(
     "Data Source=1xx.x.xx.xxx;Initial Catalog={0};;User ID=appuser;Password={1};MultipleActiveResultSets=True", 
     figDb, password); 
    var myConnection = new SqlConnection(connString); 
    myConnection.Open(); 

    foreach (var holding in _dHoldList) 
    { 
    lbStatus.Text = "Adding information to SQL for client: " + holding.ClientNo; 
    _reports.AddZeusData("tblAllBrooksHoldingsSnapshot", "CliNo, SEDOL, ISIN, QtyHeld, DateOfSnapshot", 
         "'" + holding.ClientNo + "','" + holding.Sedol + "','" + holding.ISIN + "','" + 
         holding.QuantityHeld + "','" + DateTime.Today.ToString("yyyyMMdd") + "'", false, myConnection); 
    } 
    myConnection.Close(); 
    lbStatus.Visible = false; 
} 

dHoldListDHOLDS列表;

public class DHOLDS : ExcelReport 
{ 
public String ClientNo { get; set; } 
public String Sedol { get; set; } 
public Double QuantityHeld { get; set; } 
public Double ValueOfStock { get; set; } 
public String Depot { get; set; } 
public String ValC4 { get; set; } 
public String StockR1 { get; set; } 
public String StockR2 { get; set; } 
public Double BookCost { get; set; } 
public String ISIN { get; set; } 
} 
+0

更多信息?还是必须在“_reports”中使用一些现有的答案? – SwissCoder

+0

我可以使用我自己的方法,并且数据库属于我,所以我可以添加或做任何我希望的更改。 –

回答

6

可以将您的列表映射到一个数据表,然后使用SqlBulkCopy在一次插入所有行。

+1

此方法需要您将列表转换为DataTable,然后才能保存它,但这将是从SQL Server角度来看最快的方法... – Penfold

+0

借调。我发现这是从服务器外部将数据加载到SQL Server的最快方式。 –

+0

祝你好运,如果你想获得身份值回来虽然。任何人都知道任何关系数据更好的方法吗? – Nuzzolilo

0

或者,您也可以将您的列表转换为XML,如此博客文章所述:http://charleskong.com/blog/2009/09/insert-aspnet-objects-to-sql-server/ 但SqlBulkCopy方法似乎更好。

另一种说法:如果您想通过迭代代码中的元素来解决它,而不是在单个事务中执行所有插入操作,则可能会提高性能。

+0

我正在做一个事务中的所有插入?所有循环都完成后我断开连接? –

+0

我的意思是使用SQLTransaction。通过在连接上打开一个事务,如var t = connection.BeginTransaction();将其添加到执行querry并调用t.Commit()的SQLMethod中;循环后。请参阅此stackoverflow问题的信息,为什么它可以更快:http://stackoverflow.com/questions/5091084/ado-net-sqltransaction-improves-performance – SwissCoder

1

4年后,这是我的贡献。我有同样的问题,我想批量插入,但在经过某些字段是不会在数据库中,特别是EF导航属性,所以我写了这个通用类:

/// <summary> 
/// This class is intended to perform a bulk insert of a list of elements into a table in a Database. 
/// This class also allows you to use the same domain classes that you were already using because you 
/// can include not mapped properties into the field excludedPropertyNames. 
/// </summary> 
/// <typeparam name="T">The class that is going to be mapped.</typeparam> 
public class BulkInsert<T> where T : class 
{ 
    #region Fields 

    private readonly LoggingService _logger = new LoggingService(typeof(BulkInsert<T>)); 
    private string _connectionString; 
    private string _tableName; 
    private IEnumerable<string> _excludedPropertyNames; 
    private int _batchSize; 
    private IEnumerable<T> _data; 
    private DataTable _dataTable; 

    #endregion 

    #region Constructor 

    /// <summary> 
    /// Initializes a new instance of the <see cref="BulkInsert{T}"/> class. 
    /// </summary> 
    /// <param name="connectionString">The connection string.</param> 
    /// <param name="tableName">Name of the table.</param> 
    /// <param name="data">The data.</param> 
    /// <param name="excludedPropertyNames">The excluded property names.</param> 
    /// <param name="batchSize">Size of the batch.</param> 
    public BulkInsert(
     string connectionString, 
     string tableName, 
     IEnumerable<T> data, 
     IEnumerable<string> excludedPropertyNames, 
     int batchSize = 1000) 
    { 
     if (string.IsNullOrEmpty(connectionString)) throw new ArgumentNullException(nameof(connectionString)); 
     if (string.IsNullOrEmpty(tableName)) throw new ArgumentNullException(nameof(tableName)); 
     if (data == null) throw new ArgumentNullException(nameof(data)); 
     if (batchSize <= 0) throw new ArgumentOutOfRangeException(nameof(batchSize)); 

     _connectionString = connectionString; 
     _tableName = tableName; 
     _batchSize = batchSize; 
     _data = data; 
     _excludedPropertyNames = excludedPropertyNames == null ? new List<string>() : excludedPropertyNames; 
     _dataTable = CreateCustomDataTable(); 
    } 

    #endregion 

    #region Public Methods 

    /// <summary> 
    /// Inserts the data with a bulk copy inside a transaction. 
    /// </summary> 
    public void Insert() 
    { 
     using (var connection = new SqlConnection(_connectionString)) 
     { 
      connection.Open(); 
      SqlTransaction transaction = connection.BeginTransaction(); 

      using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default | SqlBulkCopyOptions.KeepIdentity, transaction)) 
      { 
       bulkCopy.BatchSize = _batchSize; 
       bulkCopy.DestinationTableName = _tableName; 

       // Let's fix tons of mapping issues by 
       // Setting the column mapping in SqlBulkCopy instance: 
       foreach (DataColumn dataColumn in _dataTable.Columns) 
       { 
        bulkCopy.ColumnMappings.Add(dataColumn.ColumnName, dataColumn.ColumnName); 
       } 

       try 
       { 
        bulkCopy.WriteToServer(_dataTable); 
       } 
       catch (Exception ex) 
       { 
        _logger.LogError(ex.Message); 
        transaction.Rollback(); 
        connection.Close(); 
       } 
      } 

      transaction.Commit(); 
     } 
    } 

    #endregion 

    #region Private Helper Methods 

    /// <summary> 
    /// Creates the custom data table. 
    /// </summary> 
    private DataTable CreateCustomDataTable() 
    { 
     PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); 
     var table = new DataTable(); 
     foreach (PropertyDescriptor prop in properties) 
     { 
      // Just include the not excluded columns 
      if (_excludedPropertyNames.All(epn => epn != prop.Name)) 
      {     
       if (prop.PropertyType.Name == "DbGeography") 
       { 
        var type = typeof(SqlGeography); 
        table.Columns.Add(prop.Name, type); 
       } 
       else 
       { 
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); 
       } 
      } 
     } 
     foreach (T item in _data) 
     { 
      DataRow row = table.NewRow(); 
      foreach (PropertyDescriptor prop in properties) 
      { 
       // Just include the values in not excluded properties 
       if (_excludedPropertyNames.All(epn => epn != prop.Name)) 
       { 
        if (prop.PropertyType.Name == "DbGeography") 
        {       
         row[prop.Name] = SqlGeography.Parse(((DbGeography)prop.GetValue(item)).AsText()).MakeValid(); 
        } 
        else 
        { 
         row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; 
        } 
       } 
      } 
      table.Rows.Add(row); 
     } 
     return table; 
    } 

    #endregion 

} 

它的使用会如:

//1st.- You would have a colection of entities: 
var myEntities = new List<MyEntity>(); 
// [...] With thousands or millions of items 

// 2nd.- You would create the BulkInsert: 
myEntityTypeBulk = new BulkInsert<MyEntity>(_connectionString, "MyEntitiesTableName", myEntities, new[] { "ObjectState", "SkippedEntityProperty1", "SkippedEntityProperty2" }); 

// 3rd.- You would execute it: 
myEntityTypeBulk.Insert(); 

获得的性能和该类的可重用性值得此消息。我希望它能帮助:

娟您打算如何存放将是有益的,你可以用你自己的方法

+0

什么是SqlGeography? –

+1

我应该评论说,当你在实体框架中使用地理空间数据时,解决了一个常见问题,类型不一样,你的实体将使用DbGeography,EF知道并用于此目的的类型,但要插入它进入SQL它必须是SqlGeography。 – Juan