2

I am using the Entity Framework with POCO's generated using the T4 Templates. I have the generated classes in a separate assembly.

Ok, so a very simple example:

I have a Category entity in the model which has SubCategories (1-Many with SubCategory).

When I use the following code, I get The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

Public Interface ICategoryRepository
    Inherits IRepository(Of Category)

    Function GetCategories() As IQueryable(Of Category)
    Function GetCategoryByID(ByVal ID As Integer) As Category

End Interface

Public Class CategoryRepository
    Implements ICategoryRepository

    Public Function GetCategories() As System.Linq.IQueryable(Of Business.Category) Implements ICategoryRepository.GetCategories
        Using db As New GTGContainer
            Return db.Categories
        End Using
    End Function

    Public Function GetCategoryByID(ByVal ID As Integer) As Business.Category Implements ICategoryRepository.GetCategoryByID
        Using db As New GTGContainer
            Return db.Categories.FirstOrDefault(Function(x) x.ID = ID)
        End Using
    End Function

End Class

Public Class HomeController
    Inherits System.Web.Mvc.Controller

    Private _CategoryRepository As GTG.Data.Repositories.ICategoryRepository

    Public Sub New()
        Me.New(New GTG.Data.Repositories.CategoryRepository)
    End Sub

    Public Sub New(ByVal Repository As GTG.Data.Repositories.ICategoryRepository)
        _CategoryRepository = Repository
    End Sub

    Function Index() As ActionResult
        Dim m As New HomeViewModel
        m.Categories = _CategoryRepository.GetCategories

        Return View(m)
    End Function

End Class

Public Class HomeViewModel
    Public Property Categories As List(Of GTG.Business.Category)

End Class

Any help would be great. Thanks!

gideon
  • 19,329
  • 11
  • 72
  • 113
Sam
  • 15,336
  • 25
  • 85
  • 148

2 Answers2

1

This is because your GetCategories() returns an IQueryable, which is NOT actually an in-memory collection. When your View tries to enumerate it, it attempts to hit the database, which it obviously cannot do. The easy remedy is to turn it into an in-memory collection by calling ToList() in GetCategories(). That is, db.Categories.ToList().

anon
  • 4,578
  • 3
  • 35
  • 54
  • @anon - Right I tried that and it works, but how do you deal with children collection and lazy loading them? SO say I wanted the SubCategories off the Category entity? – Sam Feb 23 '11 at 04:19
  • Well it becomes an in memory collection after you enumerate. Its not never an in memory collection. – John Farrell Feb 23 '11 at 04:19
  • Doing this would remove the ability to apply server side filtering though. So if your category list is long and you don't want them all, this is a bad way to go. – Chris Sainty Feb 23 '11 at 04:19
  • 1
    If these are the only tiers you have, then you will need to call ToList() in your repository. If you want the ability to filter (as Chris mentions), you can either have more specialized methods in your repository, or you can have another service layer that "flattens" your IQueryables into in-memory representations. It would be in that layer that lazy-loaded properties are called. – anon Feb 23 '11 at 04:32
  • To clarify: the "Using" statement is disposing of the database connection/context before the data has been requested. – Foole Feb 23 '11 at 05:19
  • @Foole - That is what I gather. I gues you would want to return List(Of T) instead, but then, the way I see it is you end up with either, huge in-memory objects, or have to go back to the repository for child collections. – Sam Feb 23 '11 at 05:21
  • This is a topic of some debate. You need to think about the context in which you are performing these queries. Is it for a publicly-viewable and busy site or is it for occasional ad-hoc reporting? Will you eventually need a web service layer that your controllers access or will you have only one client of your repository? How many developers will be working on this project? All in all, it's generally safest to start with List and then progress to IQueryable if needed. – anon Feb 23 '11 at 05:40
-1

Let the context live through the life of repository.

Public Class CategoryRepository
    Implements ICategoryRepository

    Private dbContext As GTGContainer

    Public Sub New()
        dbContext = New GTGContainer
    End Sub 

    Public Function GetCategories() As System.Linq.IQueryable(Of Business.Category) Implements ICategoryRepository.GetCategories
        Return dbContext.Categories
    End Function

    Public Function GetCategoryByID(ByVal ID As Integer) As Business.Category Implements ICategoryRepository.GetCategoryByID
        Return dbContext.Categories.FirstOrDefault(Function(x) x.ID = ID)
    End Function
End Class
amit_g
  • 30,880
  • 8
  • 61
  • 118
  • Do I have to worry about cleaning it up (Dispose)? I know the GC will get it in time, but is that bad practice? – Sam Feb 23 '11 at 04:34
  • This approach has a lot of downsides if you are not careful. In her book on the Entity Framework, Julie Lerman recommends calling ToList() on any methods that return collections (http://books.google.com/books?id=rtdgHbMeTBMC&lpg=PA723&dq=julie%20lerman%20entity%20framework%20repository&pg=PA817#v=onepage&q&f=false). Also see http://www.ytechie.com/2009/06/delayed-execution-vs-tolist-in-linq-database-queries.html and http://thinkbeforecoding.com/post/2009/01/19/Repositories-and-IQueryable-the-paging-case. Until you decide you really need IQueryable, I recommend against it. – anon Feb 23 '11 at 04:43
  • This is first step in solving the bigger problem. The way the repository was presented by the OP would not go very far. My suggestion is to solve that as first step. To present an enterprise level repository solution in a question would be very difficult if not impossible as it would involve, discussion of other concept and patterns like UoW, IoC and DI. There is nothing wrong in keeping the context inside a repository. Like other things, this comes with some responsibility. Check first [few results](http://www.google.com/search?q=entity+framework+repository) that explain it in details. – amit_g Feb 23 '11 at 06:10
  • @Sam Striano and @anon, The next step would be to manage the lifetime of context as per another question about [Repository](http://stackoverflow.com/questions/5108757/ef-4-storing-context-object-in-request-object-is-it-disposed-of). This is needed not only for lifetime management but also when we have multiple repositories and want to share context amongst them. – amit_g Feb 24 '11 at 19:08