WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

反復子メソッドと using 句

by WebSurfer 2013年6月24日 14:55

このようなトラブルに悩む人はいないかもしれませんが、yield return ステートメント を使用した反復子のメソッドで using 句を使ったデータベースアクセスを行う場合の注意点を備忘録として書いておきます。

エラーメッセージ

下のコードを見てください(SqlDataSource コントロールを利用すれば自力でこのようなコードを書く必要はありませんが、あくまで例として書きました)。

GetData が反復子のメソッドです。ここで注意しなければならないことは、このメソッドの戻り値(この例では IEnumerable<Employee> 型として定義されている employees)が foreach ループなどで反復処置されるまで GetData メソッドは呼び出されないことです。

従って、using のスコープの中で、まだ SqlDataReader が閉じられてないうちに、反復処置を完了しなければなりません。この例では、反復処置は GridView1.DataBind メソッドで行われますので、using のスコープの中で GridView1.DataBind メソッドを実行しなければなりません。

using のスコープを抜けた後で GridView1.DataBind メソッドを実行すると(下のコードでコメントアウトしたように)、その時点では SqlDataReader は閉じられているので、上の画像のように reader.Read() のところで "リーダーが閉じている場合は、Read の呼び出しは無効です。" というエラーが出ます。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data.SqlClient" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">

  public class Employee
  {
    public string ID { get; set; }
    public string Name { get; set; }
  }

  // yield return を使用した反復子のメソッド。戻り値が
  // foreach ループなどで反復処置されるまで呼び出されない。
  public IEnumerable<Employee> GetData(SqlDataReader reader)
  {
    while (reader.Read())
    {
      Employee employee = new Employee();
      employee.ID = reader["EmployeeID"].ToString();
      employee.Name = reader["LastName"].ToString();
      yield return employee;
    }
  }

  IEnumerable<Employee> employees = null;
    
  protected void Page_Load(object sender, EventArgs e)
  {
    if (!Page.IsPostBack)
    {
      string connString = 
        System.Web.Configuration.WebConfigurationManager.
        ConnectionStrings["Northwind"].ConnectionString;
      string query = 
        "SELECT EmployeeID, LastName FROM Employees";

      using (SqlConnection conn = new SqlConnection(connString))
      {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand(query, conn))
        {
          using (SqlDataReader reader = cmd.ExecuteReader())
          {
            if (reader != null)
            {
              employees = GetData(reader);

              // GetData メソッドが実行されるのは、employees が
              // foreach などによって反復処置される時。ここで 
              // 以下のようにデータバインドすれば reader が開い
              // ているうちに反復処置が完了するので問題なし。
              GridView1.DataSource = employees;
              GridView1.DataBind();
            }
          }
        }
      }

      // ここでバインドしたのではダメ。GetData メソッドは
      // using 句を抜けてから実行され、reader.Read() で
      // "リーダーが閉じている場合は、Read の呼び出しは無
      // 効です。" というエラーが出る。
      //GridView1.DataSource = employees;
      //GridView1.DataBind();
    }        
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:GridView ID="GridView1" runat="server">
    </asp:GridView>
  </div>
  </form>
</body>
</html>

Tags:

ADO.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar