2009-12-30 3 views
3

Я просто задавался вопросом, как правильно вернуть читателя из класса?Asp.Net: Возвращение читателя из класса

Мой код ниже работает, но я не уверен, что это правильно.

Также. Я не могу закрыть соединение в моем методе класса и по-прежнему получать доступ к нему со своей страницы ascx, это

что ОК?

// В моем классе у меня есть следующий способ возврата записи/чтения - это единственная запись в этом случае.

public SqlDataReader GetPost() 
    { 
     SqlConnection conn = new SqlConnection(connectionString); 
     SqlCommand cmd = new SqlCommand("con_spPost", conn); 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.AddWithValue("@blogid", blogid); 
     try 
     { 
      conn.Open(); 
      return cmd.ExecuteReader(); 
     } 
     finally 
     { 
      // conn.Close(); 
     } 
    } 

// Я затем вызвать метод GetPost в моей странице ASCX так:

protected void Page_Load(object sender, EventArgs e) 
{ 

    //instantiate our class 
    MyClass DB = new MyClass(); 

    //pass in the id of the post we want to view 
    DB.PostID = Int32.Parse(Request.QueryString["p"]); 

    ///call our GetPost method 
    SqlDataReader reader = DB.GetPost(); 

    //output the result 
    reader.Read(); 
    this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"; 
    this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>"; 
    this.viewpost.InnerHtml += reader["BlogText"].ToString(); 
    reader.Close(); 
} 

Я бы признателен за любые комментарии на мой код или советы, спасибо.

Melt

+0

hmm ... SO, похоже, испортил мой код :-( – Melt

+1

SO использует некоторые знаки препинания в качестве кодов разметки. Иногда вам приходится возиться с ним, как полезный Одед. – DOK

+0

Спасибо за исправление этого. – Melt

ответ

6

Вообще говоря, это прекрасно, чтобы вернуть читателя от метода, но потребитель читателя должен взять под свой контроль все одноразовые предметы, которые будут использоваться во время жизни читателя.

Чтобы сделать это, вы должны передать IDbConnection в метод GetPost, а затем убедитесь, что ваш вызывающий абонент располагает как устройством, так и считывателем. using ключевое слово является наиболее удобный способ сделать это:

protected void Page_Load(object sender, EventArgs e) { 

    // Create the DB, get the id, etc.  

    using (IDbConnection connection = new SqlConnection(connectionString)) 
    using (IDataReader reader = DB.GetPost(connection)) { 
     reader.Read(); 
     this.viewpost.InnerHtml = reader["BlogText"].ToString(); 
     // finishing doing stuff with the reader 
    } 
} 

Как уже отмечалось, это начинает загромождать уровень представления вашего приложения с слишком много инфраструктуры доступа к данным - так что это не уместно. Пока вы не столкнетесь с проблемой производительности или не будете отображать необоснованный объем данных, вам не следует обращаться с программами чтения данных на уровне презентации. Просто верните строку DB.GetPost и инкапсулируйте туда весь код доступа к данным.

+0

Спасибо за ответ и советы – Melt

0

Почему у вас есть веб-страница о базе данных? Почему бы не абстрагировать знания базы данных и просто вернуть список или объект с данными из базы данных? Просто кажется, что нужно много смешивания ответственности, и вы могли бы облегчить себе.

5

Чтобы убедиться в том, что соединение закрыто, замените ExecuteReader вызов следующим:

return cmd.ExecuteReader(CommandBehavior.CloseConnection); 

Вы должны также удалить тэ try/finally блок.

Кроме того, в вашем Page_Load обработчика, вы должны использовать using заявление, как это:

using (SqlDataReader reader = DB.GetPost()) { 

    //output the result 
    reader.Read(); 
    this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>" 
     + "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>" 
     + reader["BlogText"].ToString(); 
} 

Кроме того, вы должны проверить, что SQL-запрос фактически вернулся что-то, вроде этого:

if (!reader.Read()) { 
    Something's wrong 
} 

Наконец, и, самое главное,, вы должны избегать своего HTML-кода, чтобы предотвратить появление отверстий XSS, позвонив по номеру Server.HtmlEncode.

Например:

this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>" 
     + "<span class='blogheads'>" + Server.HtmlEncode(reader["BlogTitle"].ToString()) + "</span><p><p>" 
     + Server.HtmlEncode(reader["BlogText"].ToString()); 
+1

Спасибо за полезные комментарии, даже не подумав о отверстиях XSS. – Melt

+0

+1 за большой комментарий и упоминание html escape. Кроме того, я прочитал, что Textile.NET - это хороший и простой способ предотвратить появление XSS при хранении разметки в базе данных. Я имел в виду использовать его, но на самом деле у меня не было шанса (... еще). http://textilenet.codeplex.com/ –

2

Существует проблема. Ваше соединение не закрывается. Как вы знаете, вы не можете закрыть его в своем GetPost, потому что тогда у вас больше не будет данных, из-за характера DataReader. Одним из способов решения этой проблемы является включение параметра в вашем методе ExecuteReader так:

cmd.ExecuteReader(CommandBehavior.CloseConnection) 

Затем, когда ваш читатель закрыт, то соединение будет закрыто.

Существует фундаментальная проблема, связанная с возвратом datareader с помощью инкапсулированного кода, поскольку соединение должно быть открыто через все это, что затрудняет обработку ошибок. Рассмотрим (A), используя вместо этого данные, что почти так же эффективно для небольших наборов данных. Таким образом, вы можете сразу закрыть свое соединение в своем методе GetPost и не беспокоиться об этом, с очень простой обработкой ошибок. Или (B) Передайте соединение в GetPost, поэтому все синтаксис Using/Dispose и обработка ошибок для соединения явны в одном месте. Я бы предложил вариант A.

3

Вам действительно не следует смешивать доступ к данным с презентационным слоем.

Рассмотрите возможность возврата типизированного DataSet или создания бизнес-объектов и возврата их в ваш контроль.

Вот учебник: http://www.asp.net/learn/data-access/tutorial-01-cs.aspx

+0

Спасибо за ссылку, я посмотрю на это позже – Melt

0

Это очень простая архитектура. Как предложил CSharpAtl, вы можете сделать его более сложным. Однако, похоже, это работает на вас.

Одно важное дополнение, которое я бы сделал, это использовать блоки try-finally. Помещение Close в finally гарантирует, что соединение будет выпущено, даже если во время обработки возникает исключение.

SqlDataReader reader; 
try 
{ 
///call our GetPost method 
    reader = DB.GetPost(); 

    //output the result 
    reader.Read(); 
    this.viewpost.InnerHtml = "<span id='post1_CreatedDate'>" + reader["CreatedDate"].ToString() + "</span><br>"; 
    this.viewpost.InnerHtml += "<span class='blogheads'>" + reader["BlogTitle"].ToString() + "</span><p><p>"; 
    this.viewpost.InnerHtml += reader["BlogText"].ToString(); 
} 
finally 
{ 
    reader.Close(); 
} 
0

Спасибо за все большие подсказки, я решил продолжить эту тему на несколько иной, но соответствующую тему здесь: Asp.Net: Returning a DataSet from a Class

С уважением Melt

0

This article Дэн Whalin может быть хороший ресурс для вас, чтобы читать. В нем показаны основы создания n-многоуровневого приложения. Вы создаете компонент доступа к данным, объект сущности, бизнес-уровень и уровень представления. Он также использует считыватели данных sql, о которых вы спрашиваете, и он показывает хороший способ использования вспомогательного метода построения объекта.

Если вам не нравится читать статью, у него также есть pretty good video по тому же вопросу и code example, которые вы можете скачать и посмотреть различные варианты этого метода создания приложений, управляемых данными.

Удачи и надеюсь, что это поможет.

Смежные вопросы