views:

38

answers:

1

Interesting problem I ran across which makes total sense. I have a generic method like so:

public TResult Run<TResult>(Func<SqlDataReader, TResult> resultDelegate)
{
   TResult result;

   using (SqlDataReader reader = command.ExecuteReader()) // command is SqlCommand with attached SqlConnection
   {
      result = resultsDelegate(reader);
   }

   // Some other unrelated code (but that's why result has a variable)

   return result;
}

In one case, the resultDelegate's return type (TResult) is IEnumerable<object>. The problem is that the Run function returns immediately due to deferred execution, disposing the SqlDataReader. Later in the code when I try to read through the results (which the delegate does reader.Read(), I get an InvalidOperationException: Invalid attempt to call Read when reader is closed.

I'm having a hard time figuring out the best way around this. I know I can return a concrete list, but I would like to avoid that if possible. I can also move the using statement inside the delegate, but once again, if I can avoid doing that for every delegate it would be nice. Any ideas?

+5  A: 

Perhaps:

public TResult Run<TResult>(Func<SqlDataReader, TResult> resultDelegate)
{
   TResult result;

   using (SqlDataReader reader = command.ExecuteReader()) // command is SqlCommand with attached SqlConnection
   {
      result = resultsDelegate(reader);
      if (typeof(TResult) == typeof(IEnumerable<object>)) 
      {
         var enumerable = result as IEnumerable<object>;
         if (enumerable != null) 
         {
            result = enumerable.ToList();  
         }
      }
   }

   // Some other unrelated code (but that's why result has a variable)

   return result;

}
JeffN825
Interesting idea--you're forcing it to be a concrete list, but outside the scope of the delegate. I think in this case it has hidden and unintended consequences (list has to be allocated in memory). If I had to go this route I would probably throw an exception instead if the delegate returns IEnumerable; that way it's obvious what is really occurring.
Nelson
kudos JeffN825 for a very creative answer.
pstrjds
If you don't keep your reader open, there's no way of using deferred execution at all. You MUST allocate and enumerate the results in the scope of the reader.
JeffN825
Right, I understand it *must* be allocated. What I meant is I would rather do the allocating somewhere else in the code where it's more explicit and obvious. This can easily hide the fact that it has been allocated.
Nelson