【问题标题】:Asp.Net: Returning a Reader from a ClassAsp.Net:从类中返回阅读器
【发布时间】:2010-12-31 03:01:08
【问题描述】:

我只是想知道从课程中返回读者的正确方法?

我下面的代码有效,但我不确定这是否正确。

还有。我无法在我的类方法中关闭连接并仍然从我的 ascx 页面访问它,是

这样好吗?

// 在我的班级中,我有以下方法来返回记录/阅读器——在这种情况下它是一条记录。

public SqlDataReader GetPost()
    {
        SqlConnection conn = new SqlConnection(connectionString);
        SqlCommand cmd = new SqlCommand("con_spPost", conn);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@blogid", blogid);
        try
        {
            conn.Open();
            return cmd.ExecuteReader();
        }
        finally
        {
          //  conn.Close();
        }
    }

//然后我在我的 ascx 页面中调用 GetPost 方法,如下所示:

protected void Page_Load(object sender, EventArgs e)
{

    //instantiate our class
    MyClass DB = new MyClass();

    //pass in the id of the post we want to view
    DB.PostID = Int32.Parse(Request.QueryString["p"]);

    ///call our GetPost method
    SqlDataReader reader = DB.GetPost();

   //output the result
    reader.Read();
    this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
    this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
    this.viewpost.InnerHtml += reader["BlogText"].ToString();
    reader.Close();
}

感谢任何关于我的代码或提示的 cmets,谢谢。

融化

【问题讨论】:

  • hmm...SO 似乎弄乱了我的代码 :-(
  • SO 使用一些标点符号作为标记代码。有时你必须摆弄它,就像乐于助人的 Oded 所做的那样。

标签: asp.net ado.net class data-access-layer encapsulation


【解决方案1】:

一般来说,从方法中返回读取器是可以的,但是读取器的消费者需要控制所有将在读取器生命周期内使用的一次性对象。

为此,您需要将IDbConnection 传递给GetPost 方法,然后确保您的调用者同时处理连接和读取器。 using 关键字是最方便的方法:

protected void Page_Load(object sender, EventArgs e) {

    // Create the DB, get the id, etc.    

    using (IDbConnection connection = new SqlConnection(connectionString))
    using (IDataReader reader = DB.GetPost(connection)) {
        reader.Read();
        this.viewpost.InnerHtml = reader["BlogText"].ToString();
        // finishing doing stuff with the reader  
    }
}

正如其他人所指出的,这开始使您的应用程序的表示层因过多的数据访问基础设施而变得混乱 - 所以这里不合适。在您发现自己遇到性能问题或需要显示不合理数量的数据之前,您不应该在表示层中处理数据读取器。只需让DB.GetPost返回一个字符串,并将所有的数据访问代码封装在其中。

【讨论】:

    【解决方案2】:

    为确保连接已关闭,请将 ExecuteReader 调用替换为以下内容:

    return cmd.ExecuteReader(CommandBehavior.CloseConnection);
    

    您还应该删除 try / finally 块。

    此外,在您的 Page_Load 处理程序中,您应该使用 using 语句,如下所示:

    using (SqlDataReader reader = DB.GetPost()) {
    
        //output the result
        reader.Read();
        this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"
            + "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>"
            +  reader["BlogText"].ToString();
    }
    

    此外,您应该检查 SQL 查询是否确实返回了某些内容,如下所示:

    if (!reader.Read()) {
        Something's wrong
    }
    

    最后,也是迄今为止最重要的,您应该通过调用Server.HtmlEncode 转义您的 HTML 以防止 XSS 漏洞。

    例如:

        this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"
            + "<span class='blogheads'>" + Server.HtmlEncode(reader["BlogTitle"].ToString()) + "</span><p><p>"
            + Server.HtmlEncode(reader["BlogText"].ToString());
    

    【讨论】:

    • 感谢有用的 cmets ,甚至没有想过 XSS 漏洞。
    • +1 表示很棒的评论并提到了 html 转义。另外,我读到 Textile.NET 是一种在数据库中存储标记时防止 XSS 漏洞的好方法。我一直想使用它,但还没有真正有机会(......还)。 textilenet.codeplex.com
    【解决方案3】:

    您真的不应该将数据访问与表示层混为一谈。

    考虑返回一个类型化的 DataSet,或者构建业务对象并将它们返回给您的控件。

    这里有一个教程: http://www.asp.net/learn/data-access/tutorial-01-cs.aspx

    【讨论】:

    • 谢谢你的链接,我稍后再看
    【解决方案4】:

    有问题。您的连接没有被关闭。如您所知,您无法在 GetPost 中关闭它,因为由于 DataReader 的性质,您将不再拥有数据。解决此问题的一种方法是在 ExecuteReader 方法中包含一个参数,如下所示:

    cmd.ExecuteReader(CommandBehavior.CloseConnection)
    

    然后,当您的阅读器关闭时,连接将关闭。

    通过封装代码返回数据读取器存在一个基本问题,即必须通过所有这些打开连接,这使得错误处理变得棘手。考虑使用 datatable 代替 (A),这对于小型数据集几乎同样有效。通过这种方式,您可以在 GetPost 方法中立即关闭连接,而无需担心,只需进行非常简单的错误处理。或者 (B) 将连接传递到 GetPost,因此连接的所有 Using/Dispose 语法和错误处理在一个地方都是明确的。我建议选项 A。

    【讨论】:

      【解决方案5】:

      这是一个非常简单的架构。正如 CSharpAtl 所建议的,你可以让它更复杂。但是,这似乎对您有用。

      我要做的一个重要补充是使用 try-finally 块。将Close放在finally中会保证即使在处理过程中发生异常也会释放连接。

      SqlDataReader reader;
      try
      {
      ///call our GetPost method
          reader = DB.GetPost();
      
         //output the result
          reader.Read();
          this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>";
          this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>";
          this.viewpost.InnerHtml += reader["BlogText"].ToString();
      }
      finally
      {
          reader.Close();
      }
      

      【讨论】:

        【解决方案6】:

        您为什么要让网页完全了解数据库?为什么不抽象出数据库知识,只返回一个包含数据库数据的列表或对象呢?只是似乎很多责任混合在一起,你可以让自己更轻松。

        【讨论】:

          【解决方案7】:

          This article by Dan Whalin 可能是您阅读的好资源。它显示了创建 n 层应用程序的基础知识。您创建一个数据访问组件、一个实体对象、一个业务层和一个表示层。他还使用您所询问的 sql 数据阅读器,并且他展示了一种拥有对象构建辅助方法的好方法。

          如果您不喜欢阅读文章,他还有一个关于同一主题的 pretty good video 和一个 code example,您可以下载并查看这种创建数据驱动应用程序的方法的不同变体。

          祝你好运,希望这对一些人有所帮助。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-05-01
            • 2015-07-10
            • 2011-10-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-06-08
            • 2011-02-02
            相关资源
            最近更新 更多