Mivaweb
Mivaweb

Reputation: 5712

Linq to entities lazy loading

I have the following class generated by entity framework:

public partial class Branch
{
    public short Id { get; set; }
    public short CompanyId { get; set; }
    public string Code { get; set; }
    public string Title { get; set; }

    public virtual Company Ts_Companies { get; set; }
}

I have the following method which takes all of the branches out of the database:

public Branch[] LoadBranches(int companyId, int page, int limit, string search, string sort, string sortOrder)
    {
        using (var dbContext = new TimeShedulerEntities())
        {
            var _branches = (from ct in dbContext.Branches
                             where ct.Title.Contains(search) || ct.Code.Contains(search)
                             select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit);
            return _branches.ToArray();
        }
    }

In my model designer I see that the Lazy Loading is set to true, but when I iterate over the branches, the property Ts_Companies is null. Also I get the following exception:

An exception of type 'System.ObjectDisposedException' occurred in EntityFramework.dll but was not handled in user code

Additional information: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.

Am I forgetting something?

Upvotes: 1

Views: 4835

Answers (2)

Los Frijoles
Los Frijoles

Reputation: 4821

You created and disposed of the context during your function since it was inside the using statement. Each entity happens to know from which context it was created so that lazy loading is possible.

When you accessed the Ts_Companies property, the entity realized that it had not yet loaded that property since it is probably a navigation property and attempted to ask its ObjectContext (TimeShedulerEntities) to load that property. However, the context had been disposed and so that it what caused that exception.

You need to modify your query as follows to 'pre-load' the Ts_Companies:

var _branches = (from ct in dbContext.Branches.Include("Ts_Companies")
                         where ct.Title.Contains(search) || ct.Code.Contains(search)
                         select ct).OrderBy(c => c.Title).Skip((page - 1) * limit).Take(limit);

It will take possibly quite a bit longer to load depending on the size of the Ts_Companies object and how many you end up bringing back at once, but the entity will stop asking its object context to load the Ts_Companies since you would have already loaded them.

A side note: I have found that creation and disposal of object context on a per-method basis causes problems when the entities are passed outside the function. If you want to create and destroy the object context in every function, you probably want to have the function return something that is not an entity. In other words, have an object that can be constructed from an entity and has the properties you need, but don't have it reference the entity. In java these are often called Data Transfer Objects (DTOs). You lose the read-write ability of entity framework, but you don't have unexpected ObjectDisposedExceptions flying all over the place.

The problem comes when you ask an entity to be associated with another (for example, adding on entity to a ICollection property of another entity) when they come from different objectcontexts. This will cause headaches for you since you would have to manually attach the objects to the same context before performing that operation. Additionally, you lose the ability to save changes to those entities without manually attaching them to a different context.

My opinion on how I would do it:

I've found it easier to either have an object containing all of these database access functions control the lifetime of the context (i.e. have your containing object be IDisposable and during disposal, destroy the context) or simply not return entities and have the datastore be read-old, write-new essentially without any modification ability.

For example, I have my object (I will call it my data access object) with a bunch of methods for getting database objects. These methods return entities. The data access object also has a SaveChanges method which simply calls the context's SaveChanges method. The data access object contains the context in a protected property and keeps it around until the data access object itself is disposed. Nobody but the data access object is allowed to touch its context. At that point, the context is disposed by manually calling 'Dispose'. The data access object could then used inside a using statement if that is your use case.

In any case, it is probably best to avoid passing entities attached to a context outside the scope in which their context exists since entity framework keeps references to that context all over the place in the individual entities

Upvotes: 3

Selman Genç
Selman Genç

Reputation: 101681

But you didn't load your Ts_Companies, use Eager Loading instead:

var _branches = dbContext.Branches
                         .Where(b => b.Title.Contains(search) || b.Code.Contains(search))
                         .Include("Ts_Companies")
                         .OrderBy(c => c.Title)
                         .Skip((page - 1) * limit)
                         .Take(limit);

And I came across the same issue before System.ObjectDisposedException, in my MVC project and I didn't use using blocks,instead I define my context on class level.If I need to return and use an array (in my View) I use that context.If I need to just update some information then I have used using blocks.I hope this helps.

Upvotes: 0

Related Questions