简体   繁体   English

Linq以实体懒加载

[英]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. 在我的模型设计器中,我看到Lazy Loading设置为true,但是当我遍历分支时,属性Ts_Companies为null。 Also I get the following exception: 另外我得到以下异常:

An exception of type 'System.ObjectDisposedException' occurred in EntityFramework.dll but was not handled in user code EntityFramework.dll中出现“System.ObjectDisposedException”类型的异常,但未在用户代码中处理

Additional information: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. 附加信息:ObjectContext实例已被释放,不能再用于需要连接的操作。

Am I forgetting something? 我忘记了什么吗?

You created and disposed of the context during your function since it was inside the using statement. 您在函数期间创建并处理了上下文,因为它位于using语句中。 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. 当您访问Ts_Companies属性时,实体意识到它尚未加载该属性,因为它可能是导航属性并试图要求其ObjectContext( TimeShedulerEntities )加载该属性。 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: 您需要按如下方式修改查询以“预加载”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. 根据Ts_Companies对象的大小和最终一次返回的数量加载可能需要更长的时间,但实体将停止询问其对象上下文加载Ts_Companies,因为您已经加载了它们。

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). 在java中,这些通常称为数据传输对象(DTO)。 You lose the read-write ability of entity framework, but you don't have unexpected ObjectDisposedException s flying all over the place. 你失去了实体框架的读写能力,但是没有意外的ObjectDisposedException飞到各处。

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. 当您要求实体与另一个实体关联时(例如,将实体添加到另一个实体的ICollection属性)来自不同的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 (ie 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. 我发现更容易让一个包含所有这些数据库访问函数的对象控制上下文的生命周期(即让你的包含对象是IDisposable并在处理期间,破坏上下文)或者只是不返回实体并让数据存储区为read-old,write-new基本上没有任何修改能力。

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. 数据访问对象还有一个SaveChanges方法, SaveChanges调用上下文的SaveChanges方法。 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'. 此时,通过手动调用'Dispose'来处理上下文。 The data access object could then used inside a using statement if that is your use case. 然后,如果这是您的用例,则可以在using语句中使用数据访问对象。

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 在任何情况下,最好避免传递附加到其上下文所在范围之外的上下文的实体,因为实体框架在各个实体中的所有位置都保持对该上下文的引用

But you didn't load your Ts_Companies , use Eager Loading instead: 但是你没有加载你的Ts_Companies ,而是使用Eager Loading

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. 我在System.ObjectDisposedException,之前遇到了同样的问题System.ObjectDisposedException,在我的MVC项目中并没有using块,而是我在类级别定义我的上下文。如果我需要返回并使用数组(在我的视图中)我使用如果我需要更新一些信息,那么我已经using块。我希望这会有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM