2015-07-31 2 views
0

Я создал веб-приложение с asp.net, примерно 100 users используют его.Соединения SQL Server в приложении ASP.NET

Однако время от времени люди получают сообщение об ошибке, что соединение по-прежнему открыто. Указывая, что он не был закрыт должным образом.

Он появляется в случайных местах, а не в одном конкретном месте и никаких других ошибок перед ним.

Я знаю, что когда у меня есть ошибка в приложении, и он сбой без моего изящества, связанного с ошибкой, соединение остается открытым, и в основном все из-за него будут разбиваться. Это заставило меня подумать, что каждый использует тот же объект соединения, возможно ли, что 2 пользователя могут иметь идеальное время и вызвать функцию, используя DB connection, в то же время вызывая ошибку? Есть ли способ убедиться, что все используют свои собственные объекты соединения, например, помещают их в свою сессию или что-то еще?

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

Это подключение к SQL Server с использованием System.Data.SqlClient.

Найдите ниже функцию, которая генерирует ошибку. Эта функция вызывается Page_Load, перед ней ничего нет.

public static SqlConnection conn = new SqlConnection("Data Source=Server00\\SQLEXPRESS;Initial Catalog=r2;Integrated Security=true;Connect Timeout=0"); 

private void populateGameDrop() 
{ 
    try 
    { 
     conn.Open(); 
     drop_game.Items.Clear(); 

     SqlCommand cmd = conn.CreateCommand(); 
     Access ac = (Access)Session["Access"]; 
     cmd.CommandText = "Select * from dbo.Games where " + ac.GameQuery; 

     SqlDataReader r = cmd.ExecuteReader(); 

     while (r.Read()) 
     { 
      drop_game.Items.Add(new ListItem(r["name"].ToString(), r["Abbr"].ToString())); 
     } 

     conn.Close(); 
    } 
    catch (Exception exc) 
    { 
     conn.Close(); 
     Log.Error(exc.ToString()); 
     Session["Error"] = exc.ToString(); 
     Response.Redirect("~/YouBrokeIt.aspx"); 
    } 

    populateServers(); 
    SetSplitScreen(); 
} 
+0

I _think_ рекомендуется открывать и закрывать соединение по запросу. Этот [ответ] (https://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why/10588594#10588594) основан на 'DbContext' Entity Framework, но я думаю, что он применим к простому ADO , слишком. –

+1

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

+1

Добавить код того, как вы обращаетесь к «БД», используете ли вы 'using' staments? – 3dd

ответ

2

Не пытайтесь разделить SqlConnection объекты.

Попробуйте вместо этого:

private static string connString = "Data Source=Server00\\SQLEXPRESS;Initial Catalog=r2;Integrated Security=true;Connect Timeout=0"; 
private void populateGameDrop() 
{ 
    try 
    { 
     using (var conn = new SqlConnection(connString)) 
     { 
      conn.Open(); 
      drop_game.Items.Clear(); 
      using (var cmd = conn.CreateCommand()) 
      { 
       Access ac = (Access)Session["Access"]; 
       //TODO 
       //TODO - Introduce parameters to avoid SQL Injection risk 
       //TODO 
       cmd.CommandText = "Select name,Abbr from dbo.Games where " + ac.GameQuery; 
       using(SqlDataReader r = cmd.ExecuteReader()) 
       { 
        while (r.Read()) 
        { 
         drop_game.Items.Add(new ListItem(r["name"].ToString(), 
              r["Abbr"].ToString())); 
        } 
       } 
      } 
     } 
    } 
    catch (Exception exc) 
    { 
     Log.Error(exc.ToString()); 
     Session["Error"] = exc.ToString(); 
     Response.Redirect("~/YouBrokeIt.aspx"); 
    } 
    populateServers(); 
    SetSplitScreen(); 
} 

За кулисами, .NET использует концепцию под названием connection pooling, так что фактическое число реальных подключений к SQL Server сведены к минимуму. Но SqlConnection объектов не предназначены для совместного использования несколькими потоками.

+0

Вы должны * также * поместить свой 'SqlDataReader' в блок' using() {....} '(если вы уже делаете это для' SqlConnection' и 'SqlCommand '....) - и вы также должны ** параметризовать ** запрос, чтобы избежать SQL-инъекции –

+0

Я вижу: O Это очень поучительно. Похоже, что я плохо делаю много изменений в своем проекте. как и для предложения параметра, делали это по другим запросам, но это басически статический запрос. Нет ввода пользователем. Просто где переменная на ранее выполненных функциях. – Kage

0

Ваши SQL соединения не должны быть статичными, используйте следующие действия для создания их

var connectionString = "YOUR CONNECTION STRING"; 
var queryString = "SQL QUERY"; 

using (SqlConnection connection = new SqlConnection(connectionString)) 
using (SqlCommand command = new SqlCommand(queryString, connection)) 
using (SqlDataReader dateReader = command.ExecuteReader()) { 

} 

ОЧЕНЬ ВАЖНО

Вы должны использовать параметризацию SQL код открыт для SQL-инъекций ,

см Parameterize SQL query

+0

Вы должны использовать 'using() {....}' блоки для 'SqlConnection',' SqlCommand' и 'SqlDataReader' –

+0

@marc_s Хорошая точка. Я обновил свой ответ. – 3dd

1

Не размещайте код базы данных непосредственно на страницах ASPX. Создание дополнительного слоя (т. Е. DAL) позволяет протестировать методы БД без использования страницы.

Попробуйте что-нибудь подобное.

//Don't embed database logic directly in the aspx files 
public class GamesProvider 
{   
    //Put the ConnectionString in you configuration file 
    private string ConnectionString 
    { 
     get { return ConfigurationManager.ConnectionStrings["GameDB"].ConnectionString; } 
    } 

    public IEnumerable<Game> LoadGames(string x, string y) 
    { 
     var games = new List<Game>(); 

     const string queryString = "select name, Abbr from dbo.Games where x = @x and y = @y"; 

     using (var connection = new SqlConnection(ConnectionString)) 
     using (var command = new SqlCommand(queryString, connection)) 
     { 
      command.Parameters.AddWithValue("@x", x); 
      command.Parameters.AddWithValue("@y", y); 
      using (var dateReader = command.ExecuteReader()) 
      { 
       while (dateReader.Read()) 
       { 
        var game = new Game 
        { 
         Name = dateReader["name"].ToString(), 
         Abbr = dateReader["Abbr"].ToString(), 
        }; 
        games.Add(game); 
       } 
      } 
     } 

     return games; 
    } 
} 

//Use types 
public class Game 
{ 
    public string Name { get; set; } 
    public string Abbr { get; set; } 
} 
Смежные вопросы