我花了很多时间弄乱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 列。这似乎起到了作用。