views:

285

answers:

3

Hello everybody.

I'm struggling with the ADO.NET Entity Framework. This is my ER-diagram:

---------------------        -----------------        ---------------
| GrandParentEntity |<-------| ParentEntity  |<-------| ChildEntity |
+-------------------+ 1    N +---------------+ 1    N +-------------+
| Id                |        | Id            |        | Id          |
---------------------        | GrandParentFk |        | ParentFk    |
                             -----------------        ---------------

Have a question about the lifetime of an object context. Assume that i requests my data as follows:

public static List<MyParentEntity> GetMyParentEntity()
{
    using (var context = new MyObjectContext(blablabla...))
    {
        var resultSet = from e in context.MyParentEntitySet select e;
        return resultSet.ToList();
    }
}

I get a list of all my parent entities. Some time later (hours?), the user decides which one he would like to inspect. (Meanwhile, the entity object passed through several tiers of the application.) I now have to load all the details of the record:

public static void LoadChildren(MyParentEntity pEntity)
{
    using (var context = new MyObjectContext(blablabla...))
    {
        pEntity.GranParentEntityReference.Load();
        pEntity.ChildEntites.Load();
    }
}

This leads to an ObjectDisposedException, because the MyParentEntity object has lost its connection to the object context that created it. I have several possibilities to handle this:

1) I can use one and the same instance of MyObjectContext all the time and never close it. This leads to a huge waste of memory.

2) I can Detach() manually each and every single entity object (and its child and parent entities) every time I leave the "using (context)" block. And I can Attach() them every time i enter a new "using(context)" block. Leads to a lot of effort. (I my opinion a framework should reduce the effort instead of increase it.)

3) Reload and then throw away each entity every time I enter a new "using(context)" block. Leads to more (unnecessary) load on the SQL Server and also wastes memory.

4) I can load all details and all references and all references of all references and the references of those references when the application starts. (No discussion, this really is silly!)

5) ... (Did I forget an option?)

Now the big QUESTION for you: which way should i choose? Is there another way that i didn't see? Or did i misunderstand the spirit of the framework entirely?

Thanks in advance

UPDATE: It is a Windows Forms Application.

+1  A: 

I would do the second option. Just there is no need to detach every single entity, you could set the query to NoTracking and entites will not be attached to the context in first place, and you would not loose the relationships (if loaded).

 public static List<MyParentEntity> GetMyParentEntity(){
using (var context = new MyObjectContext(blablabla...))
{
    var resultSet = from e in context.MyParentEntitySet select e;
    ((ObjectQuery)resultSet).MergeOption = MergeOption.NoTracking;
    return resultSet.ToList();
}}

Second,the question is if your application is web based or windows based. If it's web based I recommend using of Singleton pattern where context would be created for each request.

 public class Singleton
 {
    public static YourContext _context;
    public static YourContext Context
    {
        get
        {
            //We are in a web app, use a request scope
            if (HttpContext.Current != null)
            {
                YourContext current_context = (YourContext)HttpContext.Current.Items["_YourContext"];

                if (current_context == null)
                {
                    current_context = new YourContext();
                    HttpContext.Current.Items.Add("_YourContext", current_context);                    
                }
                return current_context;
            }
            else
            {
                if (_context == null)
                    _context = new YourContext();

                return _context;
            }
        }
    }
}

I'm not sure what to do what is the best practice if it is not web app, so putting it in static field might not be the good thing. This framework is a bit complicated and reading how it works in depth would help to understand it better and avoid this kind of situations. I used Julia Lerman's book Programming Entity Framework, 1st Edition.

Misha N.
thanks for your post. The question is about a Windows Forms Application. No WebServices.And yes, Julia Lermans book is really good, though i didn't find any hints about how my problem should be resolved.
Chrigl
A: 

I would suggest an option 5 :

public static MyParentEntity LoadChildren(MyParentEntity pEntity)
{ 
      using (var context = new MyObjectContext(...))
      {
           pEntity = (from e in context.MyParentEntitySet.Include("GranParentEntityReference").Include("ChildEntites") 
                      where e.Id == pEntity.Id
                      select e).FirstOrDefault();
        return pEntity;
      }
}

The two .Include are juste here to make sure that the query includes them (and because I discovered it not so long ago too).

I tested it in linqpad it's now working, but if it doesn't work for you (I never ran into that case anyway), option 1 seems the better solution to me, except if you're talking about really huge amount of data or that the enduser terminal is potentially low specced.

On a complete unrelated note, you can simplify your first query (if there are nothing more in that one) :

using (var context = new MyObjectContext(blablabla...))
{
    var resultSet = context.MyParentEntitySet.ToList();
    return resultSet;
}

(But it won't solve your problem)

Bishop
thanks for your reply. What you suggests is similar to my option 4: load all data at once, no matter if I will use it or not. You have to konw: we're not talking about these three simple tables, but about more then twenty tables with up to 500k records. I really would like to use "lazy load".
Chrigl
Hmm, except if it's because I'm reading and typing this just after my breakfast but it's not the same as your solution 4. It's more like the solution you were tring to do, as it's loading the data for your current entity, at the time you want. I've only rewrite the .Load() in a way that don't raise an exception :)
Bishop
A: 

So, after reading nearly all day long in Julia Lerman's "Programming Entity Framework", I finally found a hint: In chapter 20 she talks about "Using a Long-Running Object Context". This partly covers with my options 1 and 3: The context will be held open as long as the user stays on the same view. The context disposes every time the UI switches to a new view. In the new view, a new context will be created and all entities that are reused from the recent view must be reloaded.

I for my part can live with this solution though it means a lot of refactoring for me...

Chrigl