2013-06-26 2 views
0

У меня в настоящее время небольшое приложение отправляет на сервер множество разных запросов MySQL. Моя идея заключалась в том, чтобы связать соединение, запрос и чтение с функцией только с фактическим запросом в качестве параметра.Закройте соединение MySQL вне функции

Вот что я получил:

public static MySqlDataReader mySqlRead(string cmdText) 
    { 

     string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";"; 

     MySqlConnection conn = new MySqlConnection(connString); 
     MySqlCommand command = conn.CreateCommand(); 

     command.CommandText = cmdText; 

     try 
     { 
      conn.Open(); 
      MySqlDataReader reader = command.ExecuteReader(); 
      return reader; 
     } 

     catch (MySqlException) 
     { 
      throw; 
     } 

    } 

подключить и отправить запрос здесь:

private void btnLogin_Click(object sender, EventArgs e) 
    { 
     string username = txtLogin.Text; 
     string password = ORFunc.GetMD5Hash(txtPassword.Text); 

     MySqlDataReader orRead = ORFunc.mySqlRead("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'"); 
     while (orRead.Read()) 
     { 
      MessageBox.Show(orRead["id"].ToString()); 
     } 
    } 

Работает как шарм ... НО, как вы можете видеть выше, соединение никогда не закрывался. Когда я добавляю conn.Close() за .ExecuteReader(), читатель пуст, и все после возвращения конечно бесполезно.

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

веселит,

Primus

ответ

1

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

INITIALIZE/OPEN/USE/CLOSE/DESTROY 

АДО.NET, связанных с работой, необходимой для извлечения или обновления данных.

Также у вашего кода есть серьезная проблема Sql Injection (see this famous explanation), потому что, когда вы объединяете строки для формирования текста команды, у вас нет защиты от злоумышленника, который пытается атаковать вашу базу данных

private void btnLogin_Click(object sender, EventArgs e) 
{ 
    string username = txtLogin.Text; 
    string password = ORFunc.GetMD5Hash(txtPassword.Text); 

    MySqlParameter p1 = new MySqlParameter("@uname", username); 
    MySqlParameter p2 = new MySqlParameter("@pass", pass); 
    string cmdText = "SELECT * FROM orUsers WHERE username = @uname AND pass = @pass" 
    DataTable dt = ORFunc.GetTable(cmdText, p1, p2); 
    foreach(DataRow r in dt.Rows) 
    { 
     Console.WriteLine(r["ID"].ToString()); 
    } 
} 

public static DataTable GetTable(string cmdText, params MySqlParameter[] prms) 
{ 
    string connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";"; 
    // This is the INITIALIZE part 
    using(MySqlConnection conn = new MySqlConnection(connString)) 
    using(MySqlCommand command = new MySqlCommand(cmdText, conn)) 
    { 
     // OPEN 
     conn.Open(); 
     DataTable dt = new DataTable(); 
     command.Parameters.AddRange(prms); 

     // USE 
     MySqlDataReader reader = command.ExecuteReader(); 
     dt.Load(reader); 
     return dt; 
    } // The closing brace of the using statement is the CLOSE/DESTROY part of the pattern 
} 

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

+0

отличный, понятный и используемый ... Также благодаря Расселу !!! – PrimuS

2

У меня была аналогичная проблема в JAVA в последнее время, но я думаю, что то же самое будет работать для вас. По существу, вы можете создать класс, представляющий объект «SqlCall» (или что-то еще). Класс имел бы доступные члены, включая соединение и результаты. Ctor для класса возьмет текст запроса.

Тогда вам нужно будет создать новый экземпляр этого класса, запустить запрос в методе этого класса (который будет устанавливать и/или возвращать результаты), GET результаты, а затем, когда вы выполните вызов close() на вашем классе (который затем должен быть закодирован таким образом, чтобы он закрывал соединение, проведенное внутри).

Технически лучший способ сделать это - РАСШИРИТЬ сам класс подключения, но поскольку вы новичок в C#, я не буду вдаваться в подробности этого.

Как я писал код ниже, я понял, что, возможно, не ответил на ваш вопрос. Но нет никакого смысла в поддержке сейчас, так вот что у меня есть:

public class SqlCall { 

    private static connString = "server=" + ORVars.sqlServerAddr + ";port=" + ORVars.sqlServerPort + ";uid=" + ORVars.sqlServerUID + ";pwd=" + ORVars.sqlServerPass + ";database=" + ORVars.sqlServerDB + ";"; 
    private MySqlConnection conn; 
    private MySqlCommand command; 
    private MySqlDataReader reader; 

    public SqlCall(String query) { 

     conn = new MySqlConnection(connString); 
     command = conn.CreateCommand(); 
     command.CommandText = query; 

    } 

    public MySqlDataReader execute() throws Exception { 
     conn.Open(); 
     reader = command.ExecuteReader(); 
     return reader; 
    } 

    public void close() { 
     reader.close(); 
     conn.close(); 
    } 

} 

Ваш код Логин будет:

private void btnLogin_Click(object sender, EventArgs e) { 
    string username = txtLogin.Text; 
    string password = ORFunc.GetMD5Hash(txtPassword.Text); 

    SqlCall sqlcall = new SqlCall("SELECT * FROM orUsers WHERE username = '" + username + "' AND pass = '" + password + "'"); 

    try { 
     MySqlDataReader orRead = sqlcall.execute(); 
     while (orRead.Read()) 
     { 
      MessageBox.Show(orRead["id"].ToString()); 
     } 
     sqlcall.close(); 
    } catch (Exception ex) { 
     // dostuff 
    } 
} 

Дело в том, если вы не копировать данные в новый DataTable в то очень скоро вам нужно будет открыть соединение.

Отдельная записка, ВАШ КОД, ПРЕДНАЗНАЧЕН ДЛЯ ИНЪЕКЦИЙ SQL. Не знаете, что это такое? Пример: если я сказал, что мое имя пользователя: ';DROP TABLE orUsers;--, тогда вся ваша база данных пользователей исчезнет. Изучите хранимые процедуры, если вы хотите (очень здоровый) способ обойти это.