在EntityFramework中使用SqlQuery来构成真正的IQueryable

有没有办法让SqlQuery或任何其他sql执行方法组成一个模型对象,并允许像Include这样的方法工作? 我这样做(其中view_products是一个SQL表函数):

var p = context.SqlQuery("select * from view_products(@param1)", new SqlParameter("param1", "somevalue")); 

我想这样做:

 var list = p.Include(i => i.Inventories).ToList(); 

这让我得到了一个DbRawSqlQuery。 这个我不能“包含”,因为你不能在DbRawSqlQuery上做到这一点。 所以,我最后添加一个AsQueryable,如:

 var p = context.SqlQuery("select * from view_products(@param1)", new SqlParameter("param1", "somevalue")).AsQueryable(); 

但是,我知道这真的给了我一个IEnumerable,所以.Include不会产生任何结果。

我正在寻找另一种方法的建议,我从SQL函数中获取我的产品,然后加载其相关的Inventory对象,以便我有一个对象图。 谢谢!

SqlQuery提出了一些我还没有意识到的问题。

不包括在内

您正确解释了为什么Include在将IEnumerable转换为IQueryable后没有任何效果。 Include扩展了一个表达式,并且IEnumerable没有表达式(并且在将它转换为IQueryable后不会神奇地获得一个表达式)。

没有附加没有延迟加载

因此,您最终得到的Product列表未附加到上下文中,并且无法进行延迟加载 。 您必须明确地将Product附加到上下文(例如,通过将其状态设置为Unchanged )以使其延迟加载。

这是我刚刚发现的,而且很奇怪。 实体被实现为代理对象,但与DbSet.AsNoTracking()提取的分离实体相反,它们不显示延迟加载。 我认为这是一个错误,我报告了它。

但延迟加载会导致n + 1查询反模式:分别为每个产品获取Inventory对象的查询。 因此,这不是获取对象图的最佳方式。

没有关系修复

所以你真正想要的是一次性加载属于这些产品的所有Inventory对象,让关系fixup完成它的工作(即EF自动填充所有Product.Inventories集合):

 context.Inventories.Where(i => productIds.Contains(i.ProductId)).Load(); 

…其中productIdsp中的procuct ID列表。

但是,关系修正仅适用于附加实体,因此,您必须明确地将其状态Unchanged为“未Unchanged 。 如果你这样做,你就准备好了…除非你没有。

DbSet.AsNoTracking()获取的分离实体再次存在差异。 后者响应关系修复,来自SqlQuery那些没有!

(相反)精心设计的解决方案

剩下的就是:加载上面显示的相关Inventory对象并自己填充Product.Inventories集合:

 var query = from product in p join inv context.Inventories.Local on product.ProductId equals inv.ProductId into inventories select new { product, inventories } foreach(var anon in query) { anon.product.Inventories = anon.inventories.ToList(); } 

当然这完全不能令人满意。