我花了很多時間搞CommandBuilder
,包括在CommandText中手動插入「Id」並在參數中添加一個Id,但我無法讓它工作。
所以我決定暫時放棄這一點,只是自己滾動插入查詢。下面的代碼似乎適用於我向其投放的幾個示例場景,但我可能會在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值的情況下執行插入操作,它將失敗,並顯示以下錯誤:
重複的值不能插入唯一索引
我猜的SQL Server CE搞清楚時,下一個ID應該是什麼不只是使用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,我有點麻煩理解。你是說我需要手動編寫插入語句,還*設置'AutoIncrement = false'? – devuxer
對不起我的法語不好:)我的意思是這是不可能的,因爲AutoIncrement = true。只有當你改變爲AutoIncrement = false時,它才能工作。 – ibram
@ibtram,我實際上已經嘗試過,但插入命令仍然缺少「Id」。 – devuxer