I'm coming from Delphi where I have to explicitly create and destroy
instances of objects. I've been working through a C#/ASP.NET book, and many
of the examples repeat the same SqlConnection, SqlDataAdapter etc. objects,
so I thought I'd create a class with a bunch of factory methods to create my
classes for me. But, I'm unclear about how garbage collection works, and if
it is safe to do this. It seems to compile, but am I asking for trouble?
For example:

At what point will a connection created by MyRepeatedConn() be available to
be destroyed? From what (I think) I've read, the connection could be
"garbage collected" as soon as MyRepeatedConn() exits (although the garbage
collection will likely happen later on). Am I in danger of my code in
DoSomething() failing, or is the garbage collector smart enough to know that
cnn holds a reference to the SqlConnection in question, and therefore will
leave it alone until DoSomething() has also exited?

Yes, GC is smart enough to know that cnn holds a reference to an
SqlConnection and therefore will not collect it until after DoSomething has
finished. The rule is that any object that can be reached by your code is
not eligible for garbage collection. only unreachable objects are.

Your problem will not be that sql connections are getting collected too
early, it will be that they are collected too late. because the garbage
collector only collects objects when it needs to free memory, so the
connection will not be close immediately after DoSomething. And until the
unused connection is collected, the connection to the database will remain
open, and having too many connections open is expensive. For example you can
reach the limit of maximum connections allowed in the connection pool,
license problems or other problems.

To solve this, make sure that the connection is closed after you are
finished using it.

I'm coming from Delphi where I have to explicitly create and destroy instances of objects. I've been working through a C#/ASP.NET book, and
many of the examples repeat the same SqlConnection, SqlDataAdapter etc.
objects, so I thought I'd create a class with a bunch of factory methods to create
my classes for me. But, I'm unclear about how garbage collection works, and
if it is safe to do this. It seems to compile, but am I asking for trouble? For example:

At what point will a connection created by MyRepeatedConn() be available
to be destroyed? From what (I think) I've read, the connection could be "garbage collected" as soon as MyRepeatedConn() exits (although the
garbage collection will likely happen later on). Am I in danger of my code in DoSomething() failing, or is the garbage collector smart enough to know
that cnn holds a reference to the SqlConnection in question, and therefore will leave it alone until DoSomething() has also exited?

it is safe to do this. It seems to compile, but am I asking for trouble?
The only potential problem I see here is that a SqlConnection must be closed
when you are done with it. By returning the connection from a method like
that you should have it well documented that the receiver is responsible for
closing the connection.
For example:

At what point will a connection created by MyRepeatedConn() be available
to be destroyed?
When there are no longer any more reference to it.

From what (I think) I've read, the connection could be "garbage collected" as soon as MyRepeatedConn() exits (although the
garbage collection will likely happen later on). Am I in danger of my code in DoSomething() failing, or is the garbage collector smart enough to know
that cnn holds a reference to the SqlConnection in question, and therefore will leave it alone until DoSomething() has also exited?

An object will not be garbage collected as long as there is a reference to
it.

For example, if you instantiate an object in a method, use it temporarily,
and don't pass it back to the caller or assign it to another reference, then
that object will go out of scope and will be eligible for garbage collection
after the method exits. In your case, you don't have to worry about your
SqlConnection object being collected right away because you passed its
reference, from MyRepeatedConn() back to the caller, DoSomething(), who is
holding on to it. Now, since DoSomething() declared cnn, which is a
reference to the new SqlConnection, that SqlConnection object will exist
until DoSomething() exits. If DoSomething assigns the cnn reference to
another reference with a wider scope, then that same SqlConnection object
will exist as long as the reference it is assigned to. If you wanted to
make SqlConnection eligible for garbage collection before it naturally went
out of scope, you would set its reference to null and as long as there
weren't any other references, the garbage collector could collect the
SqlConnection object when it was ready to do so.