当用户登录到网站时,我试图获取用户的信息,当我使用DataSet
时它就成功了,但是如果我想使用SqlDataReader
,错误是:Invalid attempt to read when reader is closed
。我搜索了为什么是这样的,我找到了一篇文章说
SqlDataReader
要求连接保持打开状态,以便从服务器获取数据,而DataSet
不需要要求连接保持打开。
我的问题是:我也想知道如何使用SqlDataReader
?这样,当我想从数据库中获取数据时,就不必一直依赖DataSet
。
我的问题是,当我试图改变使用SqlDataReader
读取数据函数的结构时,它可以随时重用。
以下是代码:
DatabaseManager类:
public SqlDataReader GetInformationDataReader(string procName, SqlParameter[] parameters)
{
SqlDataReader reader = null;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
reader = cmd.ExecuteReader();
}
}
return reader;
}
网络管理器类:
public ModelContexts.InformationContext GetInformation(string username)
{
SqlDataReader reader = null;
ModelContexts.InformationContext context = new ModelContexts.InformationContext();
SqlParameter[] parameters =
{
new SqlParameter("@Username", SqlDbType.NVarChar, 50)
};
parameters[0].Value = username;
try
{
reader = DatabaseManager.Instance.GetInformationDataReader("GetInformation", parameters);
while(reader.Read())
{
context.FirstName = reader["FirstName"].ToString();
context.LastName = reader["LastName"].ToString();
context.Email = reader["Email"].ToString();
}
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message);
}
return context;
}
控制器:
public ActionResult MainMenu(ModelContexts.InformationContext context, string firstName, string lastName, string username, string email)
{
context = WebManager.Instance.GetInformation(User.Identity.Name);
firstName = context.FirstName;
lastName = context.LastName;
username = User.Identity.Name;
email = context.Email;
return View(context);
}
模型包含带有getter和setter (FirstName、LastName和Email)的字符串返回值。
视图包含来自模型的FirstName、LastName和电子邮件的html标签和编码。
感谢你的回答。
谢谢。
发布于 2015-07-15 19:05:35
下面是一种可以用来保持代码非常干净的方法,它允许您在连接仍然打开时从SqlDataReader
读取。它利用了通过代表的优势。希望代码是可以理解的。您可以调整它以满足您的特定需求,但希望它说明了另一个可供您使用的选项。
public void GetInformationDataReader(string procName, SqlParameter[] parameters, Action<SqlDataReader> processRow)
{
SqlDataReader reader = null;
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(procName, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
if (parameters != null)
{
foreach(SqlParameter parameter in parameters)
{
cmd.Parameters.Add(parameter);
}
}
using (SqlDataReader dataReader = cmd.ExecuteReader())
{
while (dataReader.Read())
{
// call delegate here.
processRow(dataReader);
}
}
}
}
return reader;
}
public ModelContexts.InformationContext GetInformation(string username)
{
SqlDataReader reader = null;
ModelContexts.InformationContext context = new ModelContexts.InformationContext();
SqlParameter[] parameters =
{
new SqlParameter("@Username", SqlDbType.NVarChar, 50)
};
parameters[0].Value = username;
try
{
// Instead of returning a reader, pass in a delegate that will perform the work
// on the data reader at the right time, and while the connection is still open.
DatabaseManager.Instance.GetInformationDataReader(
"GetInformation",
parameters,
reader => {
context.FirstName = reader["FirstName"].ToString();
context.LastName = reader["LastName"].ToString();
context.Email = reader["Email"].ToString();
});
}
catch(Exception ex)
{
throw new ArgumentException(ex.Message);
}
return context;
}
简要说明:
您会注意到,代码的总体结构非常类似于您已经拥有的代码。唯一的变化是:
GetInformationDataReader()
方法不是返回一个Action<SqlDataReader>
,而是接受一个Action<SqlDataReader>
委托的。GetInformationDataReader()
方法中,当连接仍处于打开状态时,将在正确的时间调用委托。GetInformationDataReader()
的调用被修改为以委托的形式传递代码块。这种模式对于这些情况可能是有用的。它使代码可重用,保持代码的整洁和分离,并且不会阻止您从using
结构中受益,以避免资源/连接泄漏。
发布于 2015-07-15 18:59:00
您已经将SqlConnection
对象包装在一个using
子句中,因此在它的末尾调用SqlConnect.Dispose
,关闭连接。无论调用者正在使用什么SqlDataReader
,都不再具有打开的连接,因此您将得到错误信息。
虽然DataSet不需要连接,但连接仍然是打开的。
这不是完全正确的。DataSet
只是一个通常由SqlDataAdapter
(该类的Fill()
方法)调用时填充的对象。SqlDataAdapter
处理SqlConnection
的开始和结束,这很可能是该评论声明的原因。但是处理这个问题的是一个不同的类,而不是DataSet
本身。将DataSet
仅仅看作是保存SqlCommand
结果集的对象。
回答你的评论.
所以,我不应该在这件事上使用关键字吗?在所有的Sql关键字中?
我也不会这么做的。使用该模型,您可以很容易地出现连接泄漏错误,并且耗尽池连接可能是一件不太有趣的故障排除。
通常,最好先使用数据,然后关闭/释放连接。有句俗语说:“开门晚,关门早”。这就是你通常想要的方法。对于您正在处理的这个问题,我不会尝试在类方法之间传递一个SqlDataReader
对象。解决方法(保持连接打开)非常容易出错。
另一个过程,回到我们提到的东西,不要使用SqlDataReader
。通过读取每一行循环循环对您没有好处。根据结果集的不同,只需填充一个DataSet
(或者通常更合适的是一个DataTable
),然后返回那个Data[Set | Table]
,或者更好的返回一个更能代表它所涉及的数据的对象。
https://stackoverflow.com/questions/31444632
复制