Entity Framework 4.1 - 动态预加载

Posted

技术标签:

【中文标题】Entity Framework 4.1 - 动态预加载【英文标题】:Entity Framework 4.1 - Dynamic Eager Loading 【发布时间】:2011-12-20 20:32:44 【问题描述】:

我有一个实体框架模型(为了简单起见,排除了一些属性):

public class Media

    public int MediaID  get; set; 
    public ICollection<Track> Tracks  get; set; 
    public ICollection<RelatedMedia> RelatedMedias  get; set; 

然后我有我的 DbContext:

public class MediaServiceContext : DbContext

    public DbSet<Media> Medias  get; set; 

然后我可以使用以下方法检索数据,效果很好:

public Media Media_Get(int id)
    
        using (MediaServiceContext mc = new MediaServiceContext())
        
            return mc.Medias.Include("Tracks").Include("RelatedMedias").Single(m => m.MediaID == id);
        
    

我的问题是,在某些情况下,我可能不想加载一个或两个相关实体,这取决于我的应用程序的哪个部分正在调用此代码;如何使包含动态?

我试过这个:

public Media Media_Get(int id, bool includeRelated, bool includeTracks)
                      
        using (MediaServiceContext mc = new MediaServiceContext())
        
            IQueryable<Media> query = mc.Medias;

            if (includeRelated)
                query = query.Include("RelatedMedias");

            if (includeTracks)
                query = query.Include("Tracks");

            return query.Single(m => m.MediaID == id);
        
    

...但我得到一个“指定的强制转换无效”异常。

我也尝试过this 提出的解决方案,但它会产生'无法将 DbQuery 转换为 ObjectQuery' 异常。将链接解决方案中的扩展方法从 '(ObjectQuery)source' 更改为 '(DbQuery)source' 然后会导致相同的 'Specified cast in not valid' 例外。

我一直在寻找解决方案,但没有运气。任何帮助将不胜感激。

修正 - 这是堆栈跟踪:

   at System.Data.SqlClient.SqlBuffer.get_Int64()
   at lambda_method(Closure , Shaper )
   at System.Data.Common.Internal.Materialization.Coordinator.HasNextElement(Shaper shaper)
   at System.Data.Common.Internal.Materialization.Shaper`1.RowNestedResultEnumerator.MoveNext()
   at System.Data.Common.Internal.Materialization.Shaper`1.ObjectQueryNestedEnumerator.TryReadToNextElement()
   at System.Data.Common.Internal.Materialization.Shaper`1.ObjectQueryNestedEnumerator.MoveNext()
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
   at API.Areas.V1.Models.RetailerManager.Media_Get(Int32 id, String retailerKey, Boolean includeLicenses, Boolean includeProperties, Boolean includeRelated, Boolean includeTracks) in C:\Users\garth\Documents\Development\WebApplications\api\Areas\V1\Models\RetailerManager.cs:line 28
   at API.Areas.V1.Controllers.RetailerController.Media(Nullable`1 id, String httpVerb, Boolean includeLicenses, Boolean includeProperties, Boolean includeRelated, Boolean includeTracks) in C:\Users\garth\Documents\Development\WebApplications\api\Areas\V1\Controllers\RetailerController.cs:line 25
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)

【问题讨论】:

Imo 它应该真的有效。你能测试使用Include 的强类型版本:query = query.Include(m =&gt; m.RelatedMedias);query = query.Include(m =&gt; m.Tracks);。它在运行时应该没有什么区别,但如果它的行为不同,我们可能会看到更多。 是的,我已经试过了,恐怕会产生相同的结果。 我已经测试了这样一个例子,它对我有用。对于您的示例代码,“Specified cast in not valid”对我来说似乎毫无意义。你能显示完整的异常消息和堆栈跟踪吗? 我完全同意,这就是为什么这让我发疯的原因。请参阅上面修改的堆栈跟踪。 您的堆栈跟踪似乎指向一个 long to int32 问题。 【参考方案1】:

您的堆栈跟踪显示.SingleOrDefault() 导致了此异常,但我在您的代码中没有看到.SingleOrDefault()

我确实看到了:

return query.Single(m => m.MediaID == id);

Media.MediaID 是否有可能是 long 而不是 int

更新

作为回答您最初问题的另一种选择,我answered a question a couple of weeks ago in relation to this。我的答案中的示例代码与动态排序有关,但我们使用非常相似的模式进行动态预加载(请参阅我的答案后的第一条评论)。

而不是像这样的方法签名:

public Media Media_Get(int id, bool includeRelated, bool includeTracks)

您的签名看起来更像这样:

public Media Media_Get(MediaGetter mediaGetter)

...你会这样使用它:

var media = someInstance.Media_Get(
    new MediaGetter  ID = id, 
        .EagerLoad(m => m.Tracks)
        .EagerLoad(m => m.RelatedTracks)
);

【讨论】:

感谢您的回复。我的堆栈跟踪不完全匹配,因为我后来修改了它,在此期间我尝试了不同的东西,即...... .Single 以及 .SingleOrDefault。然而,它们都产生相同的结果。关于 MediaID 属性,这是一个 Int。关于您提出的解决方案,当我在圣诞节/新年假期后回到办公室时,我会仔细研究一下,但是 .EagerLoad 方法不会只是在查询对象上执行 .Include 会可能为我产生相同的结果?再次感谢。 @gmeister_99,是的,您必须先修复此异常。在数据库中,MediaID 是存储为 int 还是 bigint?正如 keni 提到的,你在堆栈的顶部有这个: System.Data.SqlClient.SqlBuffer.get_Int64() 再次感谢您的回复。我已经分解了实体关系并发现了实际上不在Media 对象上而是在相关Track 对象上的违规属性。该属性实际上是一个long,而它本应是一个int。我原来的解决方案现在可以正常工作了:)

以上是关于Entity Framework 4.1 - 动态预加载的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 4.1 InverseProperty 属性和ForeignKey

Entity Framework 4.1 Fluent API 属性

卸载 Entity Framework 4.1 六月 CTP

通过 Entity Framework 4.1 中的用户定义函数进行热切加载

Entity Framework 4.1 - 映射错误的模式

Entity Framework 4.1 - 非键列之间的关系