使用EntityFramework预加载所有导航属性

16

我正在使用带有DI和IoC的存储库模式。

我在我的存储库中创建了一个函数:

T EagerGetById<T>(Guid id, string include) where T : class
{
    return _dbContext.Set<T>().Include(include).Find(id);
}

这将会急切地加载我的实体中的一个导航属性。
但是如果我的实体看起来像这样:
public class Blog : PrimaryKey
{
    public Author Author {get;set;}
    public ICollection<Post> Posts {get;set;}
}

我该如何对AuthorPosts进行预加载?我是否需要这样做:

_dbContext.Set<T>().Include("Author").Include("Posts").Find(id);

不可避免地会产生这样的一个函数:
T EagerGetById<T>(Guid id, string include, string include2, string include3) where T : class
{
    return _dbContext.Set<T>().Include(include).Include(include2).Include(include3).Find(id);
}

因为这对于一个通用存储库来说是非常低效的!

2个回答

27

如果您不想使用字符串,您也可以通过使用一个返回要预先加载的导航属性的表达式来对任意数量的包含进行相同的操作。(原始来源在此)

public IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] includeProperties) 
{
   IQueryable<TEntity> queryable = GetAll();
   foreach (Expression<Func<TEntity, object>> includeProperty in includeProperties) 
   {
      queryable = queryable.Include<TEntity, object>(includeProperty);
   }

   return queryable;
}

1
我实际上正在考虑这样做,但在使用依赖注入和控制反转的存储库模式中是否容易? - Callum Linington
仔细看一下... context.Configuration.ProxyCreationEnabled = false; 并不会强制预加载。它只是关闭代理创建。你仍然需要使用 include。我将其从答案中删除。 - Daniel Auger
这有意义吗?启用代理创建有什么意义?它能带来哪些好处?! - Callum Linington
代理创建使虚拟属性的延迟加载成为可能。实际上,EF会为导航属性放置“替身类”,当访问时会命中数据库。如果未启用代理生成,则那些未在查询中“包含”的子对象将显示为空,并且EF不会在后台命中数据库进行延迟加载。 - Daniel Auger
1
我很难理解这个实现。你能给一个使用例子吗? - Xipooo

0

如果您需要所有的导航属性,那么您只能从数据库中读取它们。您可以在查询中使用Include,或者预先将其读取到DbSet的本地数据中。

如果您想要将多个包含项传递给方法,只需像这样定义:

T EagerGetById<T>(Guid id, params string[] includes)

您的用户可以调用 EagerGetById(id, "inc1", "inc2", ...)

在方法内部,只需为includes数组中的每个元素调用Include

您应该阅读关于params关键字的内容。


请查看修改,特别是为此需要创建的新函数类型... - Callum Linington
哦,那不是低效的,只是很丑陋。我会编辑我的回答。 - zmbq
我知道所有关于参数的事情...但实际上并没有回答问题!抱歉,伙计,这不够好! - Callum Linington
记录一下,@zmbq的回答已经很好了。看起来你没有理解。被采纳的答案几乎是一模一样的。只是用属性引用代替属性名称可能更加“干净”一些。 - Dinerdo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接