【问题标题】:Inserting all rows of a DataTable into a SQL Server table, including Id values将 DataTable 的所有行插入到 SQL Server 表中,包括 Id 值
【发布时间】:2011-06-21 02:42:19
【问题描述】:

我正在尝试使用 ADO.NET 将DataTable 逐字 的内容插入到 SQL Server 数据库中。 (注意:我目前使用的是 SQL Server CE,但我想要一个适用于常规或 CE 的解决方案。)

使用以下代码会导致命令不包含Id 列:

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

这会导致Id 自动生成,这不是我想要的。我需要将每个Id 完全复制到DataTable 中。

在使用GetInsertCommand() 时,有什么方法可以强制SqlCeCommandBuilder 在插入命令中包含Id 字段?如果没有,是否有其他解决方案?我愿意使用 ADO.NET 或 EntityFramework。

请注意,Id 列是带有AutoIncrement = true 的主键。

编辑

我想出了一个我认为是解决这个问题的方法,但这最终只是产生了另一个问题(请参阅下面的答案)。所以,我仍在寻找一个好的答案。

编辑 2

我刚刚编辑了我的答案。我想我找到了解决方法。仍然笨重且特定于数据库。更喜欢合适的 ADO.NET 或 EntityFramework 解决方案。

【问题讨论】:

    标签: c# .net sql-server ado.net sql-server-ce


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      您将无法插入 Id,尽管您将手动准备插入语句,直到更改表架构 (AutoIncrement=false)。

      【讨论】:

      • 嗨,ibram,我在理解上有点困难。你是说我需要手动编写插入语句并设置AutoIncrement = false
      • 抱歉我的法语不好 :) 我的意思是这是不可能的,因为 AutoIncrement=true。仅当您更改为 AutoIncrement=false 时它才应该起作用。
      • @ibtram,我确实试过了,但插入命令中仍然缺少Id
      猜你喜欢
      • 2011-09-17
      • 1970-01-01
      • 2011-01-29
      • 1970-01-01
      • 2018-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-25
      相关资源
      最近更新 更多