2011-06-21 3 views
2

DataTable の内容をADO.NETを使用してSQL Serverデータベースに挿入しようとしています。 (注:現時点ではSQL Server CEを使用していますが、標準またはCEで動作するソリューションをご希望です)DataTableのすべての行をId値を含むSQL Serverテーブルに挿入する

次のコードを使用すると、Id列を含まないコマンドが生成されます。

var builder = new SqlCeCommandBuilder(); 
builder.DataAdapter = adapter; 
builder.SetAllValues = true; 
var command = builder.GetInsertCommand(true); 

これはIdは自動生成、私が欲しいものではありませんようになります。それぞれIdDataTableと同じようにコピーする必要があります。

GetInsertCommand()を使用すると、SqlCeCommandBuilderに挿入コマンドにIdフィールドを含める方法はありますか?そうでない場合、別の解決策がありますか?私はADO.NETまたはEntityFrameworkを使用しています。

Id列は、AutoIncrement = trueのプライマリキーであることに注意してください。

編集

私はこの解決策だと思ったものを思い付いたが、それはちょうど(以下私の答えを参照)別の問題を作成することになりました。だから、私はまだこれに対する良い答えを探しています。

編集2

私はちょうど私の答えを編集しました。私は回避策を見つけたと思う。まだclunkyデータベース特有の。適切なADO.NETまたはEntityFrameworkソリューションを好むでしょう。

答えて

2

手動で "Id"をCommandTextに挿入し、Idにパラメータを追加するなど、多くの時間をCommandBuilderで悩ましましたが、動作させることができませんでした。

私はその時点でそれをあきらめて、挿入クエリを自分自身でロールアウトすることに決めました。以下のコードは、私が投げたいくつかのシナリオ例ではうまくいくようですが、ItemToSqlFormattedValue()にはいくつかの変換がありません。

非常に感謝しています。より良い解決策がある場合は、回答として投稿してください。

public virtual void FillDatabaseTable(DataTable table) 
{ 
    var connection = _dbContextLocator.Current.Database.Connection; 
    connection.Open(); 
    SetIdentityInsert(table, connection, true); 
    FillDatabaseRecords(table, connection); 
    SetIdentityInsert(table, connection, false); 
    connection.Close(); 
} 

private void FillDatabaseRecords(DataTable table, DbConnection connection) 
{ 
    var command = GetNewCommand(); 
    command.Connection = connection; 
    var rawCommandText = string.Format("insert into {0} values ({1})", table.TableName, "{0}"); 
    foreach (var row in table.AsEnumerable()) 
     FillDatabaseRecord(row, command, rawCommandText); 
} 

private void FillDatabaseRecord(DataRow row, DbCommand command, string rawCommandText) 
{ 
    var values = row.ItemArray.Select(i => ItemToSqlFormattedValue(i)); 
    var valueList = string.Join(", ", values); 
    command.CommandText = string.Format(rawCommandText, valueList); 
    command.ExecuteNonQuery(); 
} 

private string ItemToSqlFormattedValue(object item) 
{ 
    if (item is System.DBNull) 
     return "NULL"; 
    else if (item is bool) 
     return (bool)item ? "1" : "0"; 
    else if (item is string) 
     return string.Format("'{0}'", ((string)item).Replace("'", "''")); 
    else if (item is DateTime) 
     return string.Format("'{0}'", ((DateTime)item).ToString("yyyy-MM-dd HH:mm:ss")); 
    else 
     return item.ToString(); 
} 

private void SetIdentityInsert(DataTable table, DbConnection connection, bool enable) 
{ 
    var command = GetNewCommand(); 
    command.Connection = connection; 
    command.CommandText = string.Format(
     "set IDENTITY_INSERT {0} {1}", 
     table.TableName, 
     enable ? "on" : "off"); 
    command.ExecuteNonQuery(); 
} 

編集

私はこれで問題を発見しました。IDENTITY_INSERTをオフにしても、SQL Server CEは次のIDの内容を把握できないため、ID値を指定せずに挿入しようとすると失敗します。

重複する値は、私は次のIDはどうあるべきかを考え出すと、SQL Server CEがちょうどMAX(Id) + 1を使用していないと思いユニークインデックス

に挿入することはできません。私は過去の挿入に基づいてそれを追跡しようとしますが、IDENTITY_INSERTをオンにして挿入を実行すると、この追跡は行われません。とにかくそれは私の感情です。

現時点では、データベースが読み取り専用の場合、または常に独自のIDを生成する場合、これは実用的なソリューションです。


編集2

は、私は、これはそれを修正する/希望を考える:

public virtual void FillDatabaseTable(DataTable table) 
{ 
    var connection = _dbContextLocator.Current.Database.Connection; 
    connection.Open(); 
    ReseedIdColumn(table, connection); 
    SetIdentityInsert(table, connection, true); 
    FillDatabaseRecords(table, connection); 
    SetIdentityInsert(table, connection, false); 
    connection.Close(); 
} 

private void ReseedIdColumn(DataTable table, DbConnection connection) 
{ 
    if (table.Rows.Count == 0) return; 
    var command = GetNewCommand(); 
    command.Connection = connection; 
    command.CommandText = string.Format(
     "alter table {0} alter column Id IDENTITY ({1}, 1)", 
     table.TableName, 
     table.AsEnumerable().Max(t => t.Field<int>("Id")) + 1); 
    command.ExecuteNonQuery(); 
} 

// note: for SQL Server, may need to replace `alter table` statement with this: 
// "dbcc checkident({0}.Id, reseed, {1})" 

private void FillDatabaseRecords(DataTable table, DbConnection connection) 
{ 
    var command = GetNewCommand(); 
    command.Connection = connection; //todo combine this sequence 
    var rawCommandText = string.Format("insert into {0} values ({1})", table.TableName, "{0}"); 
    foreach (var row in table.AsEnumerable()) 
     FillDatabaseRecord(row, command, rawCommandText); 
} 

private void FillDatabaseRecord(DataRow row, DbCommand command, string rawCommandText) 
{ 
    var values = row.ItemArray.Select(i => ItemToSqlFormattedValue(i)); 
    var valueList = string.Join(", ", values); 
    command.CommandText = string.Format(rawCommandText, valueList); 
    command.ExecuteNonQuery(); 
} 

private string ItemToSqlFormattedValue(object item) 
{ 
    const string quotedItem = "'{0}'"; 
    if (item is System.DBNull) 
     return "NULL"; 
    else if (item is bool) 
     return (bool)item ? "1" : "0"; 
    else if (item is Guid) 
     return string.Format(quotedItem, item.ToString()); 
    else if (item is string) 
     return string.Format(quotedItem, ((string)item).Replace("'", "''")); 
    else if (item is DateTime) 
     return string.Format(quotedItem, ((DateTime)item).ToString("yyyy-MM-dd HH:mm:ss")); 
    else 
     return item.ToString(); 
} 

private void SetIdentityInsert(DataTable table, DbConnection connection, bool enable) 
{ 
    var command = GetNewCommand(); 
    command.Connection = connection; 
    command.CommandText = string.Format(
     "set IDENTITY_INSERT {0} {1}", 
     table.TableName, 
     enable ? "on" : "off"); 
    command.ExecuteNonQuery(); 
} 

主な違いは、私は今、Id列を再シードだということです。それはトリックを行うように見えた。

0

テーブルスキーマ(AutoIncrement = false)が変更されるまで手動でinsertステートメントを準備しますが、IDは挿入できません。

+0

こんにちは、ibram、私は少し問題を理解しています。 insert文を手動で記述し、* AutoIncrement = falseをセットする必要があると言っていますか? – devuxer

+0

私の悪いフランス人のために申し訳ありません:)私はこれが可能でないことを意味しました。なぜなら、AutoIncrement = trueのためです。 AutoIncrement = falseに変更した場合にのみ、それは機能するはずです。 – ibram

+0

@ibtram、私は実際にそれを試みましたが、 'Id'はまだinsertコマンドから抜けていました。 – devuxer

関連する問題