3

I like to wrap my data access in using statements to make myself feel good about garbage collection. I am running Visual Studio 2013 Preview and targeting .NET 4.5. I have an ApiController called WordsController as such:

public class WordsController : ApiController
{
    // GET api/<controller>
    public IEnumerable<Keyword> Get()
    {
        using (TestDataContext dc = new TestDataContext())
        {
            return dc.Keywords;
        }
    }
}

I get an error telling me that the datacontext has been disposed before accessing the data.

Changing the code to this works:

public class WordsController : ApiController
{
    // GET api/<controller>
    public IEnumerable<Keyword> Get()
    {
        TestDataContext dc = new TestDataContext();

        return dc.Keywords;
    }
}

Why does this work when not using the DataContext?

3
  • I don't have this .Net version installed, but it's easy to see what's happening if you look at the decompiled code using Reflector. Have you reported this issue? It might be a bug..
    – Oscar
    Commented Jul 25, 2013 at 13:41
  • Generally you need to access db on every function inside your class so why not just initialize with the class itself? That's how everyone is doing it anyway..
    – Stan
    Commented Jul 25, 2013 at 13:42
  • You: I don't mind using the second method But with that you're not calling Dispose on your IDisposable object dc, so that is not good practice. If the garbage collector happens (for reasons we can't control) to not collect dc soon, it could be problematic. Commented Jul 25, 2013 at 13:47

4 Answers 4

9

return dc.Keywords

is not actually executing the query/materialising the results there and then, so by the time the results are actually being enumerated (and hence it tries to run the query), the context has already been disposed.

Instead try:

using (TestDataContext dc = new TestDataContext())
        {
            return dc.Keywords.ToArray(); // execute the query and materialise NOW
        }

Just to add the proper term and reference here: Deferred Execution

1
  • Ahh! That makes perfect sense now. Thank you.
    – Mark Hagan
    Commented Jul 25, 2013 at 13:41
4

AdaTheDev's answer is spot on about why it's not working. The scope of your 'using' statement does not include the return. An alternate to the proposed answers is to make your DbContext 'TestDataContext' a private field in the API controller, and make sure you dispose it when you are done. The format for that would look like:

public class WordsController : ApiController
{
     private TestDataContext db = new TestDataContext();

     // GET api/<controller>
     public IEnumerable<Keyword> Get()
     {
         return db.Keywords;
     }

     ... other API methods

    public override void Dispose()
    {
         db.Dispose();
         base.Dispose();
    }
}

The benefit of this method is that you can use the same context over and over again in the same APIController and only instantiating it once; for a heavy DataContext this can be quite useful.

2

Use this:

    public IEnumerable<Keyword> Get()
    {
        List<Keyword> data;
        using (TestDataContext dc = new TestDataContext())
        {
            data = dc.Keywords.ToList();
        } 
        // here you can do some operations with 'data'
        return data;
    }
2
  • -1 This works but it would be useful if you explained why the change is necessary. Commented Jul 25, 2013 at 13:39
  • 2
    There's really no need to assign to data when you declare it, because that empty List<> will never be used. So consider changing the first line of your method body to List<Keyword> data;. Commented Jul 25, 2013 at 13:50
0

I think that what you're returning is a deferred query rather than the actual list of keywords so when your Action returns and the query is materialized, the data context has already been disposed.

  public class WordsController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<Keyword> Get()
        {
            using (TestDataContext dc = new TestDataContext())
            {
                return dc.Keywords;  //deferred execution
            }

        }
    } //too late to materialize the query.  The context has been disposed

I think YD1M's answer will fix your problem. One quick suggestion would be to create a simple service layer and put your data access logic there then have your controller access that service rather then accessing the datacontext directly. This won't necessary fix your problem but it would remove the database dependency from the controller and thus clean up your design a bit.

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.