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>
441d6c1b-fd5d-4c4a-8722-c0914c0288f1|1|5.0
Tags: yield
ADO.NET