In general:
In the .Net framework, nothing gets disposed automatically. Otherwise we wouldn't need the IDisposable interface. The garbage collector can only handle managed resources, so every unmanaged resource must be handled in code in the dispose method.
So as a rule, every instance of every class that is implementing the IDisposable must be disposed either explicitly by calling it's Dispose method or implicitly with the using statement.
As best practice, you should strive to use anything that implements the IDisposable interface as a local variable inside a using statment:
using(var whatever = new SomeIDisposableImplementation()) { // use the whatever variable here }
The using statement is syntactic sugar. the compiler translates it to something like this:
var whatever = new SomeIDisposableImplementation(); try { // use the whatever variable here } finally { ((IDisposable)whatever).Dispose(); }
Since the finally block as guaranteed to run regardless of whatever happens in the try block, your IDisposable instance is guaranteed to be properly disposed.
With SqlConnection specifically, in order to return the connection object back to the connection pool, you must dispose it when done (the Dispose method will also close the connection, so you don't need to explicitly close it) - So the correct use of SqlConnection is always as a local variable inside the using statement:
using(var con = new SqlConnection(connectionString)) { // do stuff with con here }