【问题标题】:How does SqlCommandBuilder do its stuff and how can I stop ReSharper's suggestion to delete it?SqlCommandBuilder 如何完成它的工作,我如何阻止 ReSharper 删除它的建议?
【发布时间】:2012-06-26 04:20:33
【问题描述】:

我不明白SqlCommandBuilder 是如何做到的。我有以下代码:

public void TestCommandBuilder()
{
    var pubsDataSet = new DataSet("Pubs");
    var pubs = ConfigurationManager.ConnectionStrings["PubsConnectionString"];
    var connection = new SqlConnection(pubs.ConnectionString);
    SqlCommand cmd = connection.CreateCommand();
    cmd.CommandType = CommandType.Text;
    cmd.CommandText = "SELECT * FROM Publishers";
    var da = new SqlDataAdapter(cmd);

    da.Fill(pubsDataSet, "publishers");
    foreach (DataRow row in pubsDataSet.Tables["publishers"].Rows)
    {
        row["pub_name"] = "Updated " + DateTime.Now.Minute + DateTime.Now.Second;
    }

    // The variable builder is not used
    var builder = new SqlCommandBuilder(da);

    da.UpdateBatchSize = 3;
    da.RowUpdated += DaRowUpdated;

    da.Update(pubsDataSet, "publishers");
}

private void DaRowUpdated(object sender, SqlRowUpdatedEventArgs e)
{
    Console.WriteLine("Rows: " + e.RecordsAffected + "\r\n");
}

变量builder 没有在任何地方使用,我也不像他们在MSDN 上那样调用GetUpdateCommand() 方法。我只创建了SqlCommandBuilder,将它传递给SqlDataAdapter。但代码运行良好。

如果你看代码,它似乎是一行

var builder = new SqlCommandBuilder(da);

可以安全删除。事实上,ReSharper 建议删除它。但如果我这样做了,代码将不再运行,因为 SqlDataAdapter 不知道如何执行更新。

在调试模式下,我注意到在执行该行之后,SqlDataAdapterUpdateCommand 属性仍然是null。从MSDN-docs 我知道SqlCommandBuilder 将自己注册到SqlDataAdapterRowUpdated 事件。

但是当那个事件被触发时它会做什么呢? SqlDataBuilder 是否真的在执行更新?

我注意到的另一件事是,如果我删除 SqlCommandBuilder,我的 DaRowUpdated 方法会在 da.Update 语句上发生 InvalidOperationException 之前触发一次。我原本没想到。我认为RowUpdated 事件仅在实际更新行时发生。

所以...三个具体问题:

  1. 如何防止 ReSharper 建议删除此行?
  2. 编写像SqlCommandBuilder 这样的类是不好的做法,其中代码没有以任何方式表明创建实例正在使用传入的SqlDataAdapter 执行某些操作?
  3. 这是design pattern吗?

【问题讨论】:

  • 卸载 resharper?这对我来说很神奇。
  • 您有许多实现IDisposable 的对象。将这些包裹在using 块中,以确保资源被确定性地释放。我会在下面给出一个关于它的外观的答案。

标签: c# resharper dataadapter sqldataadapter sqlcommandbuilder


【解决方案1】:

如果您不使用变量,则绝对没有必要。如果在它的构造函数中发生了什么有用的,你仍然可以调用它:

var builder = new SqlCommandBuilder(da);

应该是:

new SqlCommandBuilder(da);

这应该在不改变代码行为的情况下处理警告。看起来有点奇怪,如果真的有这个必要,我有点质疑SqlCommandBuilder的设计。但在实践中,这样的用法与普通的方法调用本质上是一样的。

【讨论】:

  • 也许我应该保留一个参考,所以我可以在最后处理它。
  • @comecme,很好。由于它确实实现了IDisposable,因此您应该将其包装在using 块中。
  • 将它包装在 using 语句中不会使警告消失,因为 builder 仍然没有在其他任何地方使用。但是,我可以通过编写using (new SqlCommandBuilder(da)) 将您的答案与使用using 结合起来。我不知道using 内部有可能,但确实如此。
【解决方案2】:

根据我对这个问题的评论:

    public void TestCommandBuilder()
    {
        var pubs = ConfigurationManager.ConnectionStrings["PubsConnectionString"];

        using (var pubsDataSet = new DataSet("Pubs"))
        using (var connection = new SqlConnection(pubs.ConnectionString))
        using (SqlCommand cmd = connection.CreateCommand())
        {
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = "SELECT * FROM Publishers";

            using (var da = new SqlDataAdapter(cmd))
            using (new SqlCommandBuilder(da))
            {
                da.UpdateBatchSize = 3;
                da.RowUpdated += DaRowUpdated;
                da.Fill(pubsDataSet, "publishers");
                foreach (DataRow row in pubsDataSet.Tables["publishers"].Rows)
                {
                    row["pub_name"] = "Updated " + DateTime.Now.Minute + DateTime.Now.Second;
                }

                da.Update(pubsDataSet, "publishers");
            }
        }
    }

    private void DaRowUpdated(object sender, SqlRowUpdatedEventArgs e)
    {
        Console.WriteLine("Rows: " + e.RecordsAffected);
    }

【讨论】:

  • 我认为您可能必须在更新行之前移动构建,因为您正在挂钩事件 DataRowUpdated。
  • da.Update(pubsDataSet, "publishers"); 不是在做更新吗,这是在事件挂钩之后发生的?
  • 正确后更新数据库。但是,我认为事件(RowUpdated)的连接需要在数据更改之前完成。如果您查看链接中的 MSDN 示例,您会看到它在数据适配器初始化后立即连接起来。
  • 您可以在调用da.Update() 之前立即创建SqlCommandBuilder,没有问题。你的代码仍然给我builder 上的 ReSharper 警告。但似乎可以通过使用using (new SqlCommandBuilder(da)) da.Update(pubsDataSet, "publishers"); 解决。我假设这仍然会调用Dispose,但我不确定。我可以测试一下吗?
  • @JesseC.Slicer:我尝试过使用 ILSpy,但我不知道我应该寻找什么来检查是否正在调用 Dispose。但是,我刚刚创建了自己的实现IDisposable 的类,并为其添加了一个析构函数。即使我不使用变量,我也会看到 Dispose 在执行析构函数之前被调用。所以是的,如果您不将实例分配给变量,using 语句仍会调用Dispose。如果您从答案中删除var builder = ,我会接受。
【解决方案3】:

我的理解是,在构建命令构建器对象期间,它会在 DataAdapter 上添加对自身的引用,因此它知道如何构建 CRUD 命令。

请注意您在上面发布的链接的备注下的此部分。

*

SqlDataAdapter 不会自动生成 Transact-SQL 协调对 DataSet 所做的更改与 SQL Server 的关联实例。但是,您可以创建一个 SqlCommandBuilder 对象自动生成 Transact-SQL 如果您设置 SelectCommand,则用于单表更新的语句 SqlDataAdapter 的属性。然后,任何额外的 Transact-SQL 您未设置的语句由 SqlCommandBuilder 生成。

*

【讨论】:

  • 对于 resharper - 你只需要忍受它,因为你不能在粒度级别排除这样的项目。但是我同意它应该在 using 语句中使用。
猜你喜欢
  • 2014-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-17
  • 2020-01-21
  • 1970-01-01
  • 2017-09-17
相关资源
最近更新 更多