【问题标题】:Best way to structure multiple queries in one method c# asp.net在一种方法中构造多个查询的最佳方法 c# asp.net
【发布时间】:2017-03-07 13:42:11
【问题描述】:

我有一个页面加载方法,可以将带有 sql 查询的 asp 下拉列表加载到我的 SQL Server 2012 数据库中。我是新手,基本上独立学习了我正在从事的合作项目需要做的很多事情。

我一直遇到连接未正确关闭的问题,并且我的连接池在仅适度使用我的应用程序的情况下爆炸,因此我一直在尝试改进我在后面的 c# 代码中执行查询的方式.但是我对自己的理解没有信心,所以我将发布我的代码示例,希望更流利的人能够指导我一点。

string constr = ConfigurationManager.ConnectionStrings["CurrencyDb"].ConnectionString;
                using (SqlConnection con = new SqlConnection(constr)) {
                    using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.Category")) {
                        try {
                            cmd.CommandType = CommandType.Text;
                            cmd.Connection = con;
                            con.Open();

                            //Populate Category Dropdown
                            DDCategory.DataSource = cmd.ExecuteReader();
                            DDCategory.DataTextField = "CategoryName";
                            DDCategory.DataValueField = "CategoryId";
                            DDCategory.DataBind();
                        }
                        catch (SqlException sqlex) {
                            throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
                        }

                        catch (Exception ex) {
                            throw new Exception("Error loading Category data from database. " + ex.Message);
                        }
                    }

                    using (SqlCommand cmd = new SqlCommand("SELECT * FROM dbo.SubCategory ORDER BY SubCategoryName")) {
                        try {
                            cmd.CommandType = CommandType.Text;
                            cmd.Connection = con;

                            //Populate SubCategory Dropdown
                            DDSubCategory.DataSource = cmd.ExecuteReader();
                            DDSubCategory.DataTextField = "SubCategoryName";
                            DDSubCategory.DataValueField = "SubCategoryId";
                            DDSubCategory.DataBind();
                        }
                        catch (SqlException sqlex) {
                            throw new Exception("SQL Exception loading data from database. " + sqlex.Message);
                        }

                        catch (Exception ex) {
                            throw new Exception("Error loading Subcategory data from database. " + ex.Message);
                        }
                    }
                }

以上是我的页面加载方法上的两个查询,大概8个。

我最近的错误是

已经有一个打开的 DataReader 与此命令关联,必须先关闭。

这促使我提出这个问题,我在我的 Web.config 连接字符串中设置了 MultipleActiveResultSets=true 并且我的应用程序现在可以运行了,但我觉得这似乎是一个补丁来覆盖可能是糟糕的代码。

这样做的最佳做法是什么?提前非常感谢!

【问题讨论】:

  • IMO,最好的方法是对一个查询使用一个连接。你想执行另一个查询吗?设置另一个连接。 :)
  • 使用 cmd.ExecuteReader() 或在完成后调用 DataReader 上的 Dispose

标签: c# sql asp.net sql-server datareader


【解决方案1】:

嗯,可能有多种方法,但我认为您应该将所有这些查询包装在一个存储过程中,并在您的代码中调用该 SP。而不是使用DataReader 使用DataSet 并用不同的结果集填充它。

您也可以使用 datareader 实例的NextResult() 方法获取下一个SELECT 结果并进行处理。

【讨论】:

  • 感谢您的帮助!
【解决方案2】:

对于您的直接问题,您在每个连接中使用两个 cmd。 最好使用一趟、一个命令、多个结果。

这将允许您正确关闭()您的数据读取器和您的连接....“将它们返回到池中”

由于您使用的是 SqlServer,它支持一次“行程”中的多个结果集。

SELECT * FROM dbo.Category;SELECT * FROM dbo.SubCategory

将其用作您的选择语句。

使用 .ExecuteReader() 方法。

并使用 .NextResult() 从一个结果(类别)移动到下一个结果(子类别)

您可以在此处查看更完整的示例:

https://msdn.microsoft.com/en-us/library/system.data.idatareader.nextresult(v=vs.110).aspx

或者甚至在这里使用实体框架:

https://msdn.microsoft.com/en-us/library/jj691402%28v=vs.113%29.aspx?f=255&MSPPError=-2147217396

现在是一些额外的东西。

您正在混合数据层和表示层。

您应该让您的数据层填充“Dto”(有时称为“Poco”对象),并将这些对象返回到表示层。

public class Category
 public string CategoryKey{get;set;}
 public string CategoryName{get;set;}
public ICollection<SubCategory> SubCategories {get;set;}

..

 public class SubCategory
 public string SubCategoryKey{get;set;}
 public string SubCategoryName{get;set;}

..

public class MyDataLayerObject
    public ICollection<Category> GetAllCategories()
{
    ICollection<Category> categories;
    ICollection<SubCategory> subcats;

    // write a datareader call here, and use it to populate multiple Category and SubCategory objects
// make sure you close the datareader when done
//now "match up" the subcats to its parent category
}

然后让您的表示层“绑定”到类别的 ICollection。

您可能在表示层和数据层之间有一个业务层,但至少在表示层和数据层之间。

我还在下面的链接中回答了一个问题……这与您的问题相似。它们的对象是“问题”和“答案”,其中一个问题有 0:N 个答案。

在下面的问题中找到我的答案。

Return objects with populated list properties from stored procedure

注意,您可以只使用字符串中的 2 个选择查询(my/this answer 的第二行),也就是说,您不必创建存储过程。

另一种选择是使用 nuget 获取 Microsoft.EnterpriseLibrary.Data。 这为您封装了很多很棒的实践……包括关闭连接。 您仍然需要关闭数据读取器,但代码更简洁。

【讨论】:

  • 我做了一个追加,以向您指出类似的问题和方法。
  • 非常感谢,这绝对解决了我的所有想法!感谢您添加示例。
  • 如果您坚持这种方法,将您的 IDataReaders 推入 Dto/Poco 对象,您会做得更好。当您到达那里时,它也更容易过渡到实体框架(或 NHibernate 或其他)。它的 2017 年,尽量避免使用 DataSet/DataTable 解决方案。你的代码会更好,更易于维护。有机会就看看这个:msdn.microsoft.com/en-us/library/…
【解决方案3】:

这两个应该是不同的方法。一个获取类别,一个获取子类别。然后每个都有不同的连接,你不会有这个问题。

【讨论】:

    【解决方案4】:

    您应该在 DataReader 上调用 Dispose,或者将其放在 using 语句中,以便为您调用 Dispose()。

    典型的 IDataReader 模式如下所示:

    using (IDataReader r = query.ExecuteReader())
    {
      while (r.Read())
      {
         // etc.
      }
    }
    

    当您不在 IDataReader 上调用 Dispose() 时,他无法被清理。而且我希望即使您在连接上调用 Dispose() 也无法清理您的连接,因为您的 IDataReader 仍在使用它。

    是的,每个连接只能使用一个 IDataReader。 因此,正如其他人所写,您应该将其拆分为多个方法,并且每个方法使用一个连接。

    顺便说一句: 最好不要使用“SELECT *”。只需查询您需要的字段。

    【讨论】:

    • 不关闭我的阅读器最终成为池如何填满的重要来源。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-03-07
    • 1970-01-01
    • 1970-01-01
    • 2020-10-04
    • 1970-01-01
    • 2012-01-07
    • 2010-10-09
    相关资源
    最近更新 更多