Returning in the Middle of a Using Block

returning in the middle of a using block

As several others have pointed out in general this is not a problem.

The only case it will cause you issues is if you return in the middle of a using statement and additionally return the in using variable. But then again, this would also cause you issues even if you didn't return and simply kept a reference to a variable.

using ( var x = new Something() ) { 
// not a good idea
return x;
}

Just as bad

Something y;
using ( var x = new Something() ) {
y = x;
}

Is it a good approach to call return inside using {} statement?

It's perfectly safe to call return inside your using block, since a using block is just a try/finally block.

In your example above after return true, the scope will get disposed and the value returned. return false, and scope.Complete() will not get called. Dispose however will be called regardless since it reside inside the finally block.

Your code is essentially the same as this (if that makes it easier to understand):

var scope = new TransactionScope())
try
{
// my core logic
return true; // if condition met else
return false;
scope.Complete();
}
finally
{
if( scope != null)
((IDisposable)scope).Dispose();
}

Please be aware that your transaction will never commit as there's no way to get to scope.Complete() to commit the transaction.

Return a FuncTask middle of Using block

Since you want your user to be able to work with the connection between calling BeginTransaction and CommitTransaction, then the only thing you can do is to move disposal outside of this scope:

private IDbContextTransaction _transaction;

public Func<Task> BeginTransaction()
{
_transaction = _dbContext.Database.BeginTransaction()
return CommitTransaction;
}

private async Task CommitTransaction()
{
_transaction.Commit();
_transaction.Dispose();
await Task.CompletedTask;
}

// I advice you implement IDisposable fully, but
// this will do for the sake of demonstration
public void Dispose()
{
_transaction?.Dispose();
}

And then somewhere outside you'll need to do something along the lines of:

using (var obj = TheWayYouInitializeYourObject())
{
var commit = obj.BeginTransaction();
// DO WORK
await commit();
}

or

try
{
var commit = obj.BeginTransaction();
// DO WORK
await commit();
}
finally
{
obj.Dispose();
}

Basically, your using cannot span the gap in which your user works, so they'll have to do it on their own.

It is generally a good idea to make objects that have to work with disposables disposable as well.

What happens when 'return' is called from within a 'using' block?

Yes it does. The disposing of the object occurs in a finally block which executes even in the face of a return call. It essentially expands out to the following code

var context = new linqAssignmentsDataContext();
try {
return context.Persons.Where(p => p.LastName.Contans("dahl"));
} finally {
if ( context != null ) {
context.Dispose();
}
}

Return a Disposable object for use in a using block

You've got the right approach, but seem a bit lost as to how it's right.

Consider the code that you (correctly) say can't work:

DBHandle GetDB()
{
using( var db = DatabaseObj.GetHandle() )
{
db.Open();
return db;
}
}

This code is pretty much equivalent to:

DBHandle GetDB()
{
var db = DatabaseObj.GetHandle();
try
{
db.Open();
return db;
}
finally
{
if(db != null)//not included if db is a value-type
((IDisposable)db).Dispose();
}
}

A few things of note here include that the try doesn't happen until after the assignment (the same is true of using - it doesn't save you from exceptions prior to the assignment in the using) and that db is cast to IDisposable meaning both that it can't compile if that assignment isn't valid, and also that Dispose() can be either implicitly or explicitly implemented and this will work either way.

Now of course, finally blocks will execute whether an exception occurs or not. You can't use using because it's equivalent to a finally and you want to Dispose() in your method only if an exception occurs. Hence you take the finally and turn it into a catch:

DBHandle GetDB()
{
var db = DatabaseObj.GetHandle();
try
{
db.Open();
return db;
}
catch
{
if(db != null)
((IDisposable)db).Dispose();
throw;
}
}

This is pretty much the same as you have, except for the addition of a null check (maybe you can rule out the need for it) and that I'm using the bare throw (it's generally a good idea when you are going to re-throw an exception without altering or examining it. In some cases throwing a new exception is better, in which case you should include the original as the InnerException property of that new exception, so as to provide further information to someone debugging).

So all in all, you were on the right track. Hopefully I've helped explain why.

Returning IQueryable from within a using block. Need better design

If you want to return IQueryable you can make your class that contains the GetPhoneDirectory disposable, make the PhoneBookDataContext a field, and dispose it in your dispose method.

You will then put the onus on the caller to dispose his instance of your class.

E.g.

public class MyClass : IDisposable
{
PhoneBookDataContext db;

public MyClass()
{
db = new PhoneBookDataContext();
}

public IQueryable<PhoneNumber> GetPhoneDirectory()
{
return db.PhoneNumbers.Where(d => d.Site == _site);
}

public void Dispose()
{
if (db != null)
{
db.Dispose();
db = null;
}
}
}

// Caller
using(var myClass = new MyClass())
{
var queryable = myClass.GetPhoneDirectory();
...
}

In Python, if I return inside a with block, will the file still close?

Yes, it acts like the finally block after a try block, i.e. it always executes (unless the python process terminates in an unusual way of course).

It is also mentioned in one of the examples of PEP-343 which is the specification for the with statement:

with locked(myLock):
# Code here executes with myLock held. The lock is
# guaranteed to be released when the block is left (even
# if via return or by an uncaught exception).

Something worth mentioning is however, that you cannot easily catch exceptions thrown by the open() call without putting the whole with block inside a try..except block which is usually not what one wants.

Best practice regarding returning from using blocks

I prefer the first example. Fewer variables, fewer lines of code, easier to follow, easier to maintain...

public int Foo()
{
using(..)
{
return bar;
}
}


Related Topics



Leave a reply



Submit