手動で "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列を再シードだということです。それはトリックを行うように見えた。
こんにちは、ibram、私は少し問題を理解しています。 insert文を手動で記述し、* AutoIncrement = falseをセットする必要があると言っていますか? – devuxer
私の悪いフランス人のために申し訳ありません:)私はこれが可能でないことを意味しました。なぜなら、AutoIncrement = trueのためです。 AutoIncrement = falseに変更した場合にのみ、それは機能するはずです。 – ibram
@ibtram、私は実際にそれを試みましたが、 'Id'はまだinsertコマンドから抜けていました。 – devuxer