1

When .NET first came out, I was one of many who complained about .NET's lack of deterministic finalization (class destructors being called on an unpredictable schedule). The compromise Microsoft came up with at the time was the using statement.

Although not perfect, I think using using is important to ensuring that unmanaged resources are cleaned up in a timely manner.

However, I'm writing some ADO.NET code and noticed that almost every class implements IDisposable. That leads to code that looks like this.

using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlCommand command = new SqlCommand(query, connection))
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
using (SqlCommandBuilder builder = new SqlCommandBuilder(adapter))
using (DataSet dataset = new DataSet())
{
    command.Parameters.AddWithValue("@FirstValue", 2);
    command.Parameters.AddWithValue("@SecondValue", 3);

    adapter.Fill(dataset);

    DataTable table = dataset.Tables[0];
    foreach (DataRow row in table.Rows) // search whole table
    {
        if ((int)row["Id"] == 4)
        {
            row["Value2"] = 12345;
        }
        else if ((int)row["Id"] == 5)
        {
            row.Delete();
        }
    }
    adapter.Update(table);
}

I strongly suspect I do not need all of those using statements. But without understanding the code for each class in some detail, it's hard to be certain which ones I can leave out. The results is kind of ugly and detracts from the main logic in my code.

Does anyone know why all these classes need to implement IDisposable? (Microsoft has many code examples online that don't worry about disposing many of these objects.) Are other developers writing using statements for all of them? And, if not, how do you decide which ones can do without?

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
  • You can look at the source code of these class and discover yourself if you really need to dispose them. Personally I dispose the connection and the datareader – Steve Mar 18 '18 at 17:21
  • 1
    I is not going to hurt anything by `using` – paparazzo Mar 18 '18 at 17:23
  • 1
    One option would to leave ADO.NET behind and use something more modern. 2002 is sooo long ago... – H H Mar 18 '18 at 17:23
  • But I'm quit sure that the only relevant `using(){}` here is the one with the connection. That DataSet is disposbale is silly. – H H Mar 18 '18 at 17:24
  • @Steve: Yes, I know I can look at it. But having to understand the implementation for all of these classes with enough depth and certainty to say for sure I don't need to be worried about disposing them is kind of a high bar isn't it? Certainly, it violates a key advantage to OOP where you don't have to worry about the internal implementation. – Jonathan Wood Mar 18 '18 at 17:24
  • 2
    You can't have your cake and eat it. You strongly suspect you do not need all the using statements but you don't want to inspect them. Beginning to sound like a rant to me. – paparazzo Mar 18 '18 at 17:27
  • Wish to add this link to the discussion: https://stackoverflow.com/questions/913228/should-i-dispose-dataset-and-datatable – Steve Mar 18 '18 at 17:30
  • 2
    @paparazzo: Please show me where I said I don't want to inspect them, or admit you are sounding like a rant. – Jonathan Wood Mar 18 '18 at 17:30
  • 1
    @Steve: Thanks for that link. Clearly, there are arguments on both sides of the question of if I need to worry about disposing objects like `DataSet`. – Jonathan Wood Mar 18 '18 at 17:34
  • Probably not too relevant, but: you can use `adapter.Fill(dataTable);` and use `DataTable` directly instead of `DataSet` – Camilo Terevinto Mar 18 '18 at 17:48
  • It's a little late to complain about the design of ADO.NET... The upside is that you know it won't change anymore and so you can ignore most 'best practice' rules. It is safe to only wrap the Connection, DataReader and Transaction. Your choice. – H H Mar 18 '18 at 18:15

3 Answers3

3

One part of the problem here is that ADO.NET is an abstract provider model. We don't know what a specific implementation (a specific ADO.NET provider) will need with regards to disposal. Sure, we can reasonably assume the connection and transaction need to be disposed, but the command? Maybe. Reader? Probably, not least because one of the command-flags options allows you to associate a connection's lifetime to the reader (so the connection closes when the reader does, which should logically extend to disposal).

So overall, I think it is probably fine.

Most of the time, people aren't messing with ADO.NET by hand, and any ORM tools (or micro-ORM tools, such as "Dapper") will get this right for you without you having to worry about it.


I will openly confess that on the few occasions when I've used DataTable (seriously, it is 2018 - that shouldn't be your default model for representing data, except for some niche scenarios): I haven't disposed them. That one kinda makes no sense :)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Good point about abstract types. For example, `DbCommand` implements `IDispose`, and not `SqlCommand`. As far what tables I'm using, well, sometimes we have to do what the client wants. – Jonathan Wood Mar 18 '18 at 18:27
2

If a class implements IDisposable, then you should always make sure that it gets disposed, without making any assumptions about its implementation.

I think that's the minimal amount of code that you could possibly write that ensures that your objects will be disposed. I see absolutely no problem with it, and it does not distract me at all from the main logic of my code.

If reducing this code is of paramount importance to you, then you can come up with your own replacement (wrapper) of SqlConnection whose child classes do not implement IDisposable and instead get automatically destroyed by the connection once the connection is closed. However, that would be a huge amount of work, and you would lose some precision with regards to when something gets disposed.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142
  • Ultimately, it's seven lines of code. Now, I'm not trying to stretch that into being the end of the World. But if you need several of these in a method, it *does* detract from the main logic. In the end, I doubt anything bad would happen if I wrote code like in Microsoft's examples and only disposed the connection. But in many ways I'm like you in that I think I should be thorough in following the conventions of the language. And ultimately this is probably the right answer. – Jonathan Wood Mar 18 '18 at 17:28
  • @JonathanWood As was commented in your question, you should probably ditch calling ADO.NET directly and use an ORM (Entity Framework, NHibernate) or at least a micro-ORM (Dapper). That said, you could always refactor out those usings into a function. However, I don't think anything apart from the connection needs disposal – Camilo Terevinto Mar 18 '18 at 17:31
  • _"I suppose you know ..."_ I didn't and I serisously doubt this will compile. What version of C# are you talking about? – H H Mar 18 '18 at 17:34
  • @CamiloTerevinto: I prefer to use EF and commonly do. But in order to make money, I sometimes need to work within the client's environment and chosen toolset. Such is life. – Jonathan Wood Mar 18 '18 at 17:35
  • @HenkHolterman: I didn't know either. Also, I'm stuck with Visual Studio 2010 for this project. – Jonathan Wood Mar 18 '18 at 17:40
  • Jonathan, I seemed to vaguely remember that this style of `using` is valid, and I checked here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-statement before posting. – Mike Nakis Mar 18 '18 at 17:41
  • 3
    Alas, it is only valid if the type of each `IDisposable` is the same. So, I will remove that from my answer. – Mike Nakis Mar 18 '18 at 17:42
1

Yes. almost anything in ADO.Net is implementing IDisposable, whether that's actually needed - in case of, say, SqlConnection(because of connection pooling) or not - in case of, say, DataTable (Should I Dispose() DataSet and DataTable? ).

The problem is, as noted in the question:

without understanding the code for each class in some detail, it's hard to be certain which ones I can leave out.

I think that this alone is a good enough reason to keep everything inside a using statement - in one word: Encapsulation.

In many words: It shouldn't be necessary to get intimately familiar, or even remotely familiar for that matter, with the implementation of every class you are working with. You only need to know the surface area - meaning public methods, properties, events, indexers (and fields, if that class has public fields). from the user of a class point of view, anything other then it's public surface area is an implementation detail.

Regarding all the using statements in your code - You can write them only once by creating a method that will accept an SQL statement, an Action<DataSet> and a params array of parameters. Something like this:

void DoStuffWithDataTable(string query, Action<DataTable> action, params SqlParameter[] parameters)
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    using (SqlCommand command = new SqlCommand(query, connection))
    using (SqlDataAdapter adapter = new SqlDataAdapter(command))
    using (SqlCommandBuilder builder = new SqlCommandBuilder(adapter))
    using (var table = new DataTable())
    {
        foreach(var param in parameters)
        {
            command.Parameters.Add(param);
        }
        // SqlDataAdapter has a fill overload that only needs a data table
        adapter.Fill(table);
        action();
        adapter.Update(table);
    }
}

And you use it like that, for all the actions you need to do with your data table:

DoStuffWithDataTable(
    "Select...",
    table => 
    { // of course, that doesn't have to be a lambda expression here...
        foreach (DataRow row in table.Rows) // search whole table
        {
            if ((int)row["Id"] == 4)
            {
                row["Value2"] = 12345;
            }
            else if ((int)row["Id"] == 5)
            {
                row.Delete();
            }
        }
    },
    new SqlParameter[]
    {
        new SqlParameter("@FirstValue", 2),
        new SqlParameter("@SecondValue", 3)
    }
    );

This way your code is "safe" with regards of disposing any IDisposable, while you have only written the plumbing code for that just once.

Jonathan Wood
  • 65,341
  • 71
  • 269
  • 466
Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • @JonathanWood I'm sorry if you where offended by anything I wrote, that was not my intention at all. In fact, I'm not even sure I understand your comment. I simply agreed with your observation and elaborated on that. My answer was purely an attempt to help a fellow developer, as are all my answers here. – Zohar Peled Mar 18 '18 at 23:50
  • @JonathanWood Since English is not my native language I might have confused my intended message. What I mean is that one should base their code on the surface area of the classes they use, not on the implementation. This also reflect in "program to an interface, not to an implementation". If you think there's a better way to phrase it, you are more then welcome to edit my answer. – Zohar Peled Mar 19 '18 at 00:00
  • Ok, I accept that I misunderstood the intent of your answer. – Jonathan Wood Mar 19 '18 at 00:02
  • @Jonathan Thanks! I just looked up what "can't be bothered" actually mean - slightly different then what I had in mind... If it wasn't for your first comment I would probably still be using it wrong. – Zohar Peled Mar 19 '18 at 00:06
  • Yes, my biggest complaint about .NET is that IDispose breaks, to some degree, encapsulation by requiring you to know more about the implementation of classes you use. In C++, I could declare an object at the method level and it cleaned up when the method ended. I didn't have to know if the object created GDI object or whatever. – Jonathan Wood Mar 19 '18 at 00:08