-
Notifications
You must be signed in to change notification settings - Fork 300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SqlCommand.Dispose doesn't free managed object #74
Comments
The The cached metadata is kept in case it is needed for faster reading of further rows from a returned datareader. In the case where any property of the command is changed causing it to be marked as dirty the cached metadata is dropped by nulling it, the disposable just follows this pattern. I don't see a problem with this behaviour. A command object is typically used only once in a dispose block but you can easily reuse them. I've seen a program where a single command is used throughout a multi form maintenance program, and it worked. |
Whereas marking the cache dirty on property change is required for correctness, setting its reference to null on dispose is not; it's just an optimization. Only, it makes the system slightly slower because it creates a small amount of extra work on disposal, with no benefit (since the resource in question isn't precious). It seems like you'd be better off just not overriding Looking at it more broadly, the design of |
For SqlCommand specifically you're right that the tiny amount of work in Dispose isn't really needed but it isn't incorrect either. Setting it to zero will probably take a single intrusction which isn't needed and could be removed but i suspect that leaving that zero in place and breaking the link will make a JIT sweep easier since it won't need to follow the reference, so there's probably balance there. Whether you should dispose of a command object is another question and depends on many factors. If you're specifically and only using SqlCommand then as you've said there is no requirement to do so. If you've programming to DbCommand where the implementation chould change then (as was pointed out in the SO thread) you absolutely should dispose of it because the iplementation may be written in a way that requires it. So my advice would be to be safe and always dispose disposable things unless you're absolutely sure they don't need it. Changing this single line for performance reasons would require evidence that it is beneficial. I don't think it would need to be strong since the change itself is trivial but it would need to be quantifiable. Given how much happens when you do an sql query and the amount of GC activity that occurs i doubt you could be able to produce a benchmark that showed a realistic difference. It'd be nice to get to the point where such micro optimizations are useful but i think that may be a long way in the future. |
The micro-benchmark isn't the core issue. I agree it would likely not show up. The point of not setting The benefit of eliminating Even within a method, not having disposal allows more flexible use. For example, you can use property initializers. Using property initializers in a using statement statement isn't considered valid: using (var command = new SqlCommand { Connection = c, CommandText = text, Parameters = { p1, p2 } }) { ... } Such a construction is flagged by static code evaluation because one of the property setters could throw an exception causing the using block to not be entered and If A general way to look at it is that |
Removing the override of Dispose on SqlCommand will not make it non-disposable, dispose is implemented in the base class Developers aren't required to use using blocks to handle disposable objects if they choose not to as long as they handle the lifetime appropriately it doesn't matter. |
Agreed. The design question about the developer cost of There doesn't seem to be much one can do about it. It's the price you pay for the flexibility of a single |
What exactly is the "price"? Clearing a field is not an expensive operation. Further, |
The price is the developer time and code complexity. In the best case, it's having to add a using statement. In the worst case, it means that the class using Across a whole project, maybe it's one developer hour, times 100,000 projects, times an average $60 per developer hour. Completely wild guesses, but if they're about right, the price is $6,000,000. |
...I'm not sure we could remove the interface anyways - that would be a pretty major breaking change, after all. |
That was my point about "could have been corrected in the transition to .NET Core". Fortunately, there might still a good solution: Create a custom attribute (perhaps |
.... that's not really any different than adding/removing A publicly visible attribute like that is likely to be permanent for the same general reason: breaking behavior change if removed. |
The maybe-someday approach gets expensive. Taken to its logical conclusion, it would cause a lot of other classes to add just-in-case |
As recently announced in the .NET Blog, focus on new SqlClient features an improvements is moving to the new Microsoft.Data.SqlClient package. For this reason, we are moving this issue to the new repo at https://github.com/dotnet/SqlClient. We will still use https://github.com/dotnet/corefx to track issues on other providers like System.Data.Odbc and System.Data.OleDB, and general ADO.NET and .NET data access issues. |
SqlCommand.Dispose
contains this code:This doesn't free the cached data, which is supposed to be the purpose of the Dispose(Boolean) overload when working with managed resources that don't implement
IDisposable
:By just setting
_cachedMetaData
to null, the resources are only reclaimed by the garbage collector, by which time the disposedSqlCommand
object would also likely not be referenced, meaning that disposing theSqlCommand
and setting_cachedMetaData
to null is wasted effort.This issue was observed as part of a discussion about this question: Is SqlCommand.Dispose() required if associated SqlConnection will be disposed?
The text was updated successfully, but these errors were encountered: