я получил неверную попытку вызвать чтение, когда читатель закрыт при использовании sqldatareader, как решить эту проблему в трехуровневой архитектуре

когда я нажимаю кнопку ссылки, выдается ошибка «Неверная попытка вызвать чтение, когда читатель закрыт». Мой метод DAL, который возвращает dr,

private SqlDataReader getDownload(string sql)
{
SqlDataReader dr;
using (SqlConnection con = ConnectionManager.GetDatabaseConnection())
{
SqlCommand cmd = new SqlCommand("getInfo", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@query", SqlDbType.VarChar).Value = sql;
cmd.Connection = con;
dr = cmd.ExecuteReader();
}
return dr;
}

другой метод DAL

public SqlDataReader getDownload(int auto_id)
{
string sql = "select mfile_name,file_data from Viva_Notice where auto_id=" + auto_id;
SqlDataReader dr = getDownload(sql) ;
return dr;
}

мой метод BLL

public SqlDataReader getDownload(int field)
{
GetPostAssign mGetPostAssign = new GetPostAssign();
SqlDataReader dr = mGetPostAssign.getDownload(field);
return dr;
}

когда я звоню, получаю "Неверная попытка вызвать чтение, когда читатель закрыт"

protected void lnkDownload_Click(object sender, EventArgs e)
{
try
{
LinkButton lnkbtn = sender as LinkButton;
GridViewRow gvrow = lnkbtn.NamingContainer as GridViewRow;
if (gvrow.RowIndex < 0)
return;
int field = Convert.ToInt32(lnkbtn.Attributes["RowIndex"]);
SqlDataReader dr = MclsAssignment.getDownload(field);
if (dr.Read())
{
Response.AddHeader("Content-Disposition", "attachment;filename=\"" + dr["mfile_name"] + "\"");
Response.BinaryWrite((byte[])dr["file_data"]);
Response.End();
}
}
catch (Exception)
{    
throw;
}
}

person Hitesh Kasar    schedule 12.08.2013    source источник
comment
Пожалуйста, делайте правильный отступ в своем коде и всегда используйте предыдущее, когда вы публикуете вопрос - вы должны были сказать, что он был в основном нечитаемым задолго до того, как вы опубликовали.   -  person Jon Skeet    schedule 12.08.2013
comment
Как только вы закроете соединение, вы также закроете читатель. Вам необходимо извлечь данные из считывателя перед его закрытием.   -  person Daniel Kelley    schedule 12.08.2013
comment
Возможный дубликат - stackoverflow.com/questions/6021207/   -  person vapcguy    schedule 10.09.2015


Ответы (4)


Это действительно проблема дизайна - вы не можете вернуть «живой» считыватель данных, если вы намереваетесь разрушить соединение, SqlDataReader зависит от него, т.е.

SqlDataReader dr;
using (SqlConnection con = ConnectionManager.GetDatabaseConnection())
{
    SqlCommand cmd = new SqlCommand("getInfo", con);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@query", SqlDbType.VarChar).Value = sql;
    cmd.Connection = con;
    dr = cmd.ExecuteReader();
} // the SqlConnection is disposed here
return dr; // dr is now invalid

Вдобавок ко всему, вы передаете детали реализации своим слоям BLL/UI, возвращая SqlDataReader. Вы должны читать данные, пока соединение активно, и вместо этого возвращать фактические данные, чтобы все было хорошо и чисто, например.

public class Download
{
     public string Name { get; set; }
     public byte[] Data { get; set; }
}
...
private Download getDownload(string sql)
{
    using (SqlConnection con = ConnectionManager.GetDatabaseConnection())
    using (SqlCommand cmd = new SqlCommand("getInfo", con))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@query", SqlDbType.VarChar).Value = sql;
        con.Open();
        Using (SqlDataReader dr = cmd.ExecuteReader())
        {
            while (dr.Read())
            {
                return new Download
                {
                    Name = (string)dr["mfile_name"],
                    Data = (byte[])dr["file_data"]
                };
            }
        }
    }
}
person James    schedule 12.08.2013

Это проблема дизайна с вашим уровнем доступа к данным. Это не отделяет приложение от базы данных, как предполагалось. это просто другой интерфейс к базе данных, чем использование SQL. Вам нужно полностью их разъединить. То есть вам нужно вернуть объекты данных в приложение. Затем эти объекты данных должны быть заполнены в DAL с помощью средства чтения.

Итак, ваш метод будет примерно таким:

private MyFile getDownload(string sql)
{
    SqlDataReader dr;
    using (SqlConnection con = ConnectionManager.GetDatabaseConnection())
    {
        SqlCommand cmd = new SqlCommand("getInfo", con);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@query", SqlDbType.VarChar).Value = sql;
        cmd.Connection = con;
        dr = cmd.ExecuteReader();


        return new MyFile {
                  file_name = dr["mfile_name],
                  file_data = dr["file_data]
        }
    }
}
person Rune FS    schedule 12.08.2013
comment
+1 за это проблема дизайна с вашим уровнем доступа к данным. Это не отделяет приложение от базы данных, как предполагалось. - person Anonymous; 04.04.2014

Оператор using размещает объект в контексте (в данном случае это SqlConnection) сразу после выполнения последнего оператора в блоке using. Это причина, по которой вы получаете сообщение об ошибке «читатель закрыт ...», когда вы обращаетесь к нему вне функции DAL.

Либо вы должны удалить блок using и обрабатывать открытие/закрытие соединения на более высоком уровне, либо, что еще лучше, вы должны преобразовать SqlDataReader в объект или DataTable внутри метода DAL и вместо этого вернуть его. Пример с DataTable:

private DataTable getDownload(string sql)
{
    SqlDataReader dr;
    DataTable dt;
    using (SqlConnection con = ConnectionManager.GetDatabaseConnection())
    {
        SqlCommand cmd = new SqlCommand("getInfo", con);
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@query", SqlDbType.VarChar).Value = sql;
        cmd.Connection = con;
        dr = cmd.ExecuteReader();
        dt = new DataTable();
        dt.Load(dr);
    }
    return dt;
}
person Varun K    schedule 12.08.2013
comment
bt, если я явно открою его с помощью cmd.Connection.Open(), тогда также появится ошибка - person Hitesh Kasar; 12.08.2013
comment
Попробуйте решение таблицы данных, которое я только что написал; это поможет вам. Кстати, вам также нужно будет изменить методы вызывающей стороны, чтобы работать с таблицей данных, а не с считывателем данных. - person Varun K; 12.08.2013

вы должны изменить/добавить метод

public DataTable getDownload(string sql)
{
    using (SqlConnection con = new SqlConnection(yourconstring))
    {
        SqlDataAdapter dap = new SqlDataAdapter(sql,con);
        DataTable dt = new DataTable();
        dap.Fill(dt);
        return dt;
    }
}

затем в методе загрузки нажмите

DataTable dt = MclsAssignment.getDownload(field);
        if (dt.Rows.Count> 0)
        {
            Response.AddHeader("Content-Disposition", "attachment;filename=\"" + (string)dt.Rows[0]["mfile_name"] + "\"");
            Response.BinaryWrite((byte[])dt.Rows[0]["file_data"]);
            Response.End();
        }
person Ehsan    schedule 12.08.2013
comment
Sys.WebForms.PageRequestManagerParserErrorException: сообщение, полученное от сервера, не может быть проанализировано. Распространенными причинами этой ошибки являются изменения ответа вызовами Response.Write(), фильтрами ответа, HttpModules или включенной трассировкой сервера. Подробности: ошибка при синтаксическом анализе рядом с "����JFIF��”. - person Hitesh Kasar; 12.08.2013
comment
какой номер строки? - person Ehsan; 12.08.2013
comment
он показывает окно предупреждения и не показывает номер строки. После выполнения всего кода попытки он напрямую переходит к соответствующему блоку catch. - person Hitesh Kasar; 12.08.2013
comment
Уважаемый, после Response.End() он напрямую переходит к блоку catch и показывает ошибку - person Hitesh Kasar; 12.08.2013