使用 dbcontext 和 projection 进行预加载

eager loading with dbcontext and projection

我们正在将项目从 ObjectContext 转换为 dbContext。 我们当前的问题是处理预加载的方式不同。

示例上下文

public class Person
{
    public virtual ICollection<Email> Emails { get; set; }
    public virtual ICollection<Post> Posts { get; set; }
}
public class Email
{
    public string Address{ get; set; }
}
public class Post
{
    public string Content{ get; set; }
}

我们在整个企业中有许多代码段期望加载电子邮件,因此调用 person.Emails.First() 时没有考虑它。 所以我们需要确保 Emails 被热切加载。

有时候我们就可以 Include

然而,当我们在数据层中使用投影时,我们 运行 遇到了问题。即

return context.Persons.Select(p=> new Top5VM {
    Person = p,
    TopPosts = p.Posts.Take(5)
};

我们有很多代码依赖于 Top5VM 并期望加载 Person.Emails

无论我们尝试过什么,我们都无法弄清楚将 Include(或 Load)函数调用放在哪里才能真正发挥作用。

有了 ObjectContext,我们将在 Top5VM 上有一个虚拟 属性,称为 Emails。一旦它被加载,ObjectContext 就会引用所有这些 Entities,因此即使我们通过 person 对象访问它们也不需要返回到服务器。但这不再适用于 DbContext

想通了。我需要设置 context.Configuration.LazyLoadingEnabled = false; 任何时候我想使用投影来完成预先加载。当然,除非我想直接从投影访问加载的实体。

您可以始终使用自己的 DbContext 来做到这一点:

public class MyDbContext : DbContext
{
    public MyDbContext() : base()
    {
        Configuration.LazyLoadingEnabled = false;
    }
}

然后到处使用它而不是 DbContext

或者,如果您控制了所有 POCO,则可以从相关集合中删除 virtual 关键字……virtual 关键字是 Microsoft 用于延迟加载的神奇连接.

编辑

我也倾向于将此作为扩展方法:

static class DbContextExtensions
{
    public static DbContext AsEagerLoadingContext(this IDbContext context)
    {
        context.Configuration.LazyLoadingEnabled = false;
        //context.Configuration.AutoDetectChangesEnabled = false;

        return context;
    }
}

如此使用,允许根据需要使用或不使用延迟加载:

using (var context = new DbContext().AsEagerLoadingContext())
    context.Stuff.Select(s => s.AllTheThings);