C# SqlDataReader 클래스
SqlDataReader 클래스는 SQL Server와 연결을 유지한 상태에서 한번에 한 레코드(One Row)씩 데이타를 가져오는데 사용된다. SqlCommand.ExecuteReader()로부터 리턴되는 SqlDataReader 객체는 (파일의 BOF와 같이) 첫 Row 이전에 포인터를 위치시키기 때문에 개발자는 SqlDataReader의 Read()메서드를 써서 처음 Row로 이동해 주어야 한다. DataReader는 하나의 Connection에 하나만 Open되어 있어야 하며, 사용이 끝나면 Close() 메서드를 호출하여 닫아 준다.
예제
public void Sample()
{
string strConn = "Data Source=.;Initial Catalog=pubs;Integrated Security=SSPI;";
StringBuilder sb = new StringBuilder();
using (SqlConnection conn = new SqlConnection(strConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM Product";
// SqlDataReader 객체를 리턴
SqlDataReader rdr = cmd.ExecuteReader();
// 다음 레코드 계속 가져와서 루핑
while (rdr.Read())
{
// C# 인덱서를 사용하여
// 필드 데이타 엑세스
string s = rdr["Name"] as string;
sb.Append(s);
}
// 사용후 닫음
rdr.Close();
}
//... sb 데이타 사용
}
{
string strConn = "Data Source=.;Initial Catalog=pubs;Integrated Security=SSPI;";
StringBuilder sb = new StringBuilder();
using (SqlConnection conn = new SqlConnection(strConn))
{
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM Product";
// SqlDataReader 객체를 리턴
SqlDataReader rdr = cmd.ExecuteReader();
// 다음 레코드 계속 가져와서 루핑
while (rdr.Read())
{
// C# 인덱서를 사용하여
// 필드 데이타 엑세스
string s = rdr["Name"] as string;
sb.Append(s);
}
// 사용후 닫음
rdr.Close();
}
//... sb 데이타 사용
}
Connection Leak을 방지하는 방법
ADO.NET을 사용하면 Connection Leak이 발생하는 경우가 종종 있다. 그 중의 한 케이스가 SqlDataReader을 사용할 때 흔히 발생한다. 예를 들어, 아래의 예 처럼 SqlDataReader를 리턴하는 메서드를 만든 후 이를 호출해서 사용하는 경우가 많다. 이 경우 SqlConnection 객체는 GetDataReader() 메서드를 빠져 나가면 그 SqlConnection 객체를 엑세스할 방법이 없어 진다. 나중에 SqlDataReader객체에서 SqlDataReader.Close() 를 호출한다 해도 이는 DataReader만 Close하기 때문에 Connection을 닫을 수 없게 된다. 즉, Connection Leak이 일어나는 것이다. 이러한 문제를 해결하는 방법은 ExecuteReader 메서드를 실행할 때 CommandBehavior.CloseConnection 옵션을 아래 예와 같이 주는 것이다. 이 옵션을 사용하면 SqlDataReader.Close()가 호출되었을 때, SqlConnection도 같이 Close된다.
예제
// SqlDataReader 객체를 리턴
public SqlDataReader GetDataReader()
{
SqlConnection conn = new SqlConnection(strConn);
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM Production.Product";
SqlDataReader rdr = cmd.ExecuteReader();
return rdr;
}
// SqlDataReader를 사용후 Close
private void button1_Click(object sender, EventArgs e)
{
MySample sample = new MySample();
SqlDataReader dr = sample.GetDataReader();
dr.Read();
data = dr[0].ToString();
dr.Close();
}
// 문제점 해결 방법:
// System.Data.CommandBehavior.CloseConnection
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
public SqlDataReader GetDataReader()
{
SqlConnection conn = new SqlConnection(strConn);
conn.Open();
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = "SELECT * FROM Production.Product";
SqlDataReader rdr = cmd.ExecuteReader();
return rdr;
}
// SqlDataReader를 사용후 Close
private void button1_Click(object sender, EventArgs e)
{
MySample sample = new MySample();
SqlDataReader dr = sample.GetDataReader();
dr.Read();
data = dr[0].ToString();
dr.Close();
}
// 문제점 해결 방법:
// System.Data.CommandBehavior.CloseConnection
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);