1

I have a method that returns a data reader. Normally I would wrap a using around the data reader so that it gets disposed nicely after I iterate through it. THe problem is that I've chosen to consume the data reader using Linq, which means that the defered execution causes the reader to get disposed early. Is there a proper pattern to consume and dispose of the data reader using Linq without having to built a complete collection of it's contents?

using (System.Data.SqlClient.SqlDataReader reader = CallStoredProcedure())
{
    return reader.Cast<System.Data.Common.DbDataRecord>().Select(rec => new ObjectModel.CollectorSummaryItem()
    {
        CollectorID = (int)rec[0],
        Name = rec.IsDBNull(1) ? null : rec.GetString(1),
        Information = rec.IsDBNull(2) ? null : rec.GetString(2)
    });
}
1
  • Have you tried simply delegating the read? Create a method that accepts IDataRecord and builds your object. Build a list off of it and return that list? Commented Apr 24, 2013 at 3:22

2 Answers 2

8

You need to actually read from the reader inside the using block:

using (System.Data.SqlClient.SqlDataReader reader = CallStoredProcedure())
{
    while (reader.Read())
    {
        yield return new ObjectModel.CollectorSummaryItem()
        {
            CollectorID = (int)reader[0],
            Name = reader.IsDBNull(1) ? null : reader.GetString(1),
            Information = reader.IsDBNull(2) ? null : reader.GetString(2)
        };
    }
}

This will evaluate to code with the same or consistent return type with what you had before, but doesn't close the reader until after you're done reading from it.

Sign up to request clarification or add additional context in comments.

Comments

0

Shooting from the hip here. This should help you decouple the reader logic as well.

public IEnumerable<MyObject> ExecuteNonQuery(...)
{
  ...
  using(var reader = comm.ExecuteReader())
  {
    var results = new List<MyObject>();
    return reader
            .Cast<System.Data.Common.DbDataRecord>()
            .Select(rec => GetFromReader(rec))
            .ToList();
  }
}

public MyObject GetFromReader(IDataRecord rdr)
{
   return new MyObject { Prop1 = rdr["prop1"], Prop2 = rdr["prop2"] };
}

1 Comment

There is a slight problem with that code. LINQ is lazy and won't evaluate the lambda expression until you enumerate over the IEnumerable, which will likely be outside of the method given. By that time, the using statement would have disposed of reader. If you used a method like ToList() after the Select statement to force the enumeration of the IEnumerable inside the using block, that might work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.