1

First of all: I got my code running without using oop. I declared all my variables inside the same class and opened/closed the connection right before and after passing the query to the db. That worked! Now with some new experiences I tried to split my code into different classes. Now it wont work anymore.

It tells me "Connection must be valid and open". Enough text, here's my current code:


Services.cs

public static MySqlConnection conn // Returns the connection itself
        {
            get
            {
                MySqlConnection conn = new MySqlConnection(Services.ServerConnection);
                return conn;
            }
        }

public static string ServerConnection // Returns the connectin-string
        {
            get
            {    
                return String.Format("Server={0};Port=XXXX;Database=xxx;Uid=xxx;password=xxXxxXxXxxXxxXX;", key);
            }
        }

public static void DB_Select(string s, params List<string>[] lists)
        {
            try
            {
                MySqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                string command = s;
                cmd.CommandText = command;
                MySqlDataReader sqlreader = cmd.ExecuteReader();
                while (sqlreader.Read())
                {
                    if (sqlreader[0].ToString().Length > 0)
                    {
                        for (int i = 0; i < lists.Count(); i++)
                        {
                            lists[i].Add(sqlreader[i].ToString());
                        }
                    }
                    else
                    {
                        foreach (List<string> save in lists)
                        {
                            save.Add("/");
                        }
                    }
                }
                sqlreader.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
            }
        }

LoginForm.cs

private void checkUser(string username, string password)
        {
        using (Services.conn)
                    {
                        Services.conn.Open();
                        Services.DB_Select("..a short select statement..");
                        Services.conn.Close();
                    }

I guess this is all we need. I have shortened my code to get a focus on the problem.

I created Services.cs to get a global way to access the db from all forms without copy&pasting the connection info. Now when I reach my LoginForm.cs it throws an error "Connection must be valid and open". I've already debugged my code. It's all time closed. Even when passing conn.Open() it stays closed. Why?

Another try: I've also tried placing conn.Open() and conn.Close() inside Services.DB_Select(..) at the beginning and end. Same error here.

I have to say: The code worked before and I've used the same connection-string. So the string itself is surely valid.

I appreciate any help given here!

Andrey Korneyev
  • 26,353
  • 15
  • 70
  • 71
C4d
  • 3,183
  • 4
  • 29
  • 50
  • 1
    I hope you do realize that when you call `conn` or `Services.conn`, you just create a new `MySqlConnection`with the default state `Closed`. Also storing your `MySqlConnection `in a static is generaly a bad idea. – User999999 Jan 21 '15 at 08:14
  • You need to restructure your Connection class. – Kavindu Dodanduwa Jan 21 '15 at 08:18
  • 1
    Did you tried to put the connection in `private MysqlConnection mycon = null` And constructor initialize it so when you instanciate the object it will automatically create the connection and open it like `public Service() { myconn = new MysqlConnection(Services.ServerConnection); myconn.Open(); }` ? – Anwar Jan 21 '15 at 08:18
  • 1
    @Knorriemans: it's only a bad idea if the connection instance is stored in a static field, not if a fresh connection is returned from a static (factory) method. However, that method is redundant anyway. – Tim Schmelter Jan 21 '15 at 08:21
  • Only so it doesnt looks like I wasnt interested in your comments: The questions came in so fast I couldnt focus on everything. Anyway thanks guys. – C4d Jan 21 '15 at 09:13

3 Answers3

6

The problem is that you don't store the connection that was returned from your factory property. But don't use a property like a method. Instead use it in this way:

using (var con = Services.conn)
{
    Services.conn.Open();
    Services.DB_Select("..a short select statement..", con ));
    //Services.conn.Close(); unnecessary with using
}

So use the same connection in the using that was returned from the property(or better created in the using) and pass it to the method which uses it. By the way, using a property as factory method is not best practise.

But in my opinion it's much better to create the connection where you use it, best place is in the using statement. And throw the con property to the garbage can, it is pointless and a source for nasty errors.

public static void DB_Select(string s, params List<string>[] lists)
{
    try
    {
         using(var conn = new MySqlConnection(Services.ServerConnection))
         {
            conn.Open();
            MySqlCommand cmd = conn.CreateCommand();
            cmd.CommandText = s;
            using( var sqlreader = cmd.ExecuteReader())
            while (sqlreader.Read())
            {
                if (sqlreader[0].ToString().Length > 0)
                {
                    for (int i = 0; i < lists.Count(); i++)
                    {
                        lists[i].Add(sqlreader[i].ToString());
                    }
                }
                else
                {
                    foreach (List<string> save in lists)
                    {
                        save.Add("/");
                    }
                }
            } // unnecessary to close the connection
        }     // or the reader with the using-stetement
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
    }
}
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • If I understood this right this are 2 different solutions right? Even using the first one (changing my using) or the second one (implementing it into the DB_Select). If so.. dont I have to open the connection in variant 2 too? Couldnt I just place `using(var conn = ...)){ conn.Open()`? – C4d Jan 21 '15 at 08:56
  • Answered my own question. Check out my incoming edit. (Hope its not corrupt using it this way). – C4d Jan 21 '15 at 08:59
  • @C4ud3x: yes, this are two ways but i strongly recommend the second. The property/method is completely redundant and pointless. And even the first needs to refactor your method that it takes the connection as argument which is another reason to use the second. Note that opening the connection does not mean that the physical connection needs to be openend. With enabled [connection-pool](https://msdn.microsoft.com/en-us/library/8xx3tyca(v=vs.110).aspx) it just means that this connection is tagged as "in use" whereas closing a connection means "this can be used". – Tim Schmelter Jan 21 '15 at 09:00
  • @C4ud3x: yes, of course it needs to be opened. Thanks for the edit. – Tim Schmelter Jan 21 '15 at 09:05
  • To clear out the first: I also prefer the second one. Way more usability and for sure better readability of my code where I got less to type for a simple select statement. With that line I guess I'm done here unless you got any other no-go's to correct. Really big thanks to you. Saved my day. And also brought me some new knowledge about using using. :) – C4d Jan 21 '15 at 09:07
  • I've got one last question: DB_Select ist called with a list-variable. The function inside has to grab all data out of the db found by the given string, cicle through it and save them all to the passed list. Now with this construct it is breaking after the first variable. So... sqlreader.Read() is breaking after the first run. Even if the result-set got 5 results. – C4d Jan 21 '15 at 13:19
  • 1
    @C4ud3x: are you sure that it has to do with the changes? Maybe you have to show an example in a separate question since it's not clear what you are trying to do. Also, what means that it's _breaking_? – Tim Schmelter Jan 21 '15 at 13:21
  • I'm a bit insecure with this. A bit more. But yeah, maybe I should start a new question if the problem persists. I'll try to find the problem myself first. – C4d Jan 21 '15 at 13:23
  • Hah how embarrassing. Was my mistake. I'll give you your wellearned peace now. :) – C4d Jan 21 '15 at 13:53
1
private MySqlConnection _conn;
public MySqlConnection conn // Returns the connection itself
        {
            get
            {
               if(_conn == null)
                 _conn = new MySqlConnection(Services.ServerConnection);
                return _conn;
            }
        }
User999999
  • 2,500
  • 7
  • 37
  • 63
BoeseB
  • 695
  • 4
  • 17
  • 1
    Don't use a static field for a connection, that's really a bad idea if connection pool is enabled(default). – Tim Schmelter Jan 21 '15 at 08:23
  • Also this isnt helping at all. – C4d Jan 21 '15 at 08:27
  • Well he is generating a new connection every time he accesses the Property. So he works on a new Instance when he Opens, Executes a Command and Closes the connection. Sorry i had to go to a meeting so i had no time to explain that code snippet. – BoeseB Jan 21 '15 at 09:00
  • Are you kidding me? My Company was just holding a meeting O.o. Cant be real.... you don't???... nah impossible.... – C4d Jan 21 '15 at 09:01
  • 1
    also Knorriemans made the field and property none static so i see why it wont help with your static method. But Tim Schmelter is right, using a field for a connection is not that good, cause you add a reference to the connection and so its hard to free that resource. Go with KCodd solution and add a using statement ;) – BoeseB Jan 21 '15 at 09:03
1

Try to restructure your Services class as follows

   public static MySqlConnection conn // Returns the connection itself
       {
            get
            {
                MySqlConnection conn = new MySqlConnection(Services.ServerConnection);
                return conn;
            }
        }

    private static string ServerConnection // Returns the connectin-string - PRIVATE [Improved security]
        {
            get
            {    
                return String.Format("Server={0};Port=XXXX;Database=xxx;Uid=xxx;password=xxXxxXxXxxXxxXX;", key);
            }
        }

 // Rather than executing result here, return the result to LoginForm - Future improvement
  public static void DB_Select(MySqlConnection conn ,string s, params List<string>[] lists)
        {
            try
            {
                MySqlCommand cmd = conn.CreateCommand();
                cmd.CommandType = CommandType.Text;
                string command = s;
                cmd.CommandText = command;
                MySqlDataReader sqlreader = cmd.ExecuteReader();
                while (sqlreader.Read())
                {
                    if (sqlreader[0].ToString().Length > 0)
                    {
                        for (int i = 0; i < lists.Count(); i++)
                        {
                            lists[i].Add(sqlreader[i].ToString());
                        }
                    }
                    else
                    {
                        foreach (List<string> save in lists)
                        {
                            save.Add("/");
                        }
                    }
                }
                sqlreader.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
            }
        }

In LoginForm.cs use returning connection and store it there. When you need to execute query, use

           MySqlConnection conn=Services.conn(); // Get a new connection
           Services.DB_Select(conn,"..a short select statement.."); // Executing requirement
           Services.conn.Close(); 

Additional - I suggest you need to return MySqlDataReader to LoginForm and handle results there

Kavindu Dodanduwa
  • 12,193
  • 3
  • 33
  • 46
  • Oh wow. Give me some minutes to check that out. Would you mind adding short comments where the changes are? Thanks till here. – C4d Jan 21 '15 at 08:28
  • 1
    Thanks for the extra comments. Picked out another one. Anyway upvoted for your help. :) – C4d Jan 21 '15 at 09:08