实体框架 - 停止按需延迟加载相关实体?

Posted

技术标签:

【中文标题】实体框架 - 停止按需延迟加载相关实体?【英文标题】:Entity Framework - Stop Lazy Loading Related Entities On Demand? 【发布时间】:2015-06-20 22:35:20 【问题描述】:

我已经设置了实体框架,它在我需要的大部分时间都可以正常工作。我有这样的结构

public partial class Topic : Entity

    public Guid Id  get; set; 
    public string Name  get; set; 
    public DateTime CreateDate  get; set; 
    public virtual Post LastPost  get; set; 
    public virtual Category Category  get; set; 
    public virtual IList<Post> Posts  get; set; 
    public virtual IList<TopicTag> Tags  get; set; 
    public virtual MembershipUser User  get; set; 
    public virtual IList<TopicNotification> TopicNotifications  get; set; 
    public virtual IList<Favourite> Favourites  get; set; 
    public virtual Poll Poll  get; set; 

如您所见,我有许多相关实体是列表。这些被映射为标准并且是延迟加载的,因此我可以调用 Topic.Posts 或 Topic.TopicNotifications 等...(下面的映射)

HasOptional(t => t.LastPost).WithOptionalDependent().Map(m => m.MapKey("Post_Id"));
HasOptional(t => t.Poll).WithOptionalDependent().Map(m => m.MapKey("Poll_Id"));            
HasRequired(t => t.Category).WithMany(t => t.Topics).Map(m => m.MapKey("Category_Id"));
HasRequired(t => t.User).WithMany(t => t.Topics).Map(m => m.MapKey("MembershipUser_Id"));
HasMany(x => x.Posts).WithRequired(x => x.Topic).Map(x => x.MapKey("Topic_Id")).WillCascadeOnDelete();
HasMany(x => x.TopicNotifications).WithRequired(x => x.Topic).Map(x => x.MapKey("Topic_Id")).WillCascadeOnDelete();
HasMany(t => t.Tags)
    .WithMany(t => t.Topics)
    .Map(m =>
                
                    m.ToTable("Topic_Tag");
                    m.MapLeftKey("TopicTag_Id");
                    m.MapRightKey("Topic_Id");
                );

这都是一口井。但在某些情况下,我需要手动填充 Topic.Posts 和 Topic.Favorites。

但是如果我尝试设置Topic.Posts = SomeCollection,它会触发延迟加载并首先加载所有帖子,然后让我设置我的集合,以便执行两组 sql(第一组我不想要)

有没有在我想手动设置集合时按需手动关闭延迟加载?

希望这是有道理的...:/

【问题讨论】:

我在这里感觉到一个 XY 问题meta.stackexchange.com/a/66378/134199 - 我认为您真正的问题是“需要手动填充 Topic.Posts 和 Topic.Favorites”。您可能正在尝试以不同的方式完成更好的事情,因为您正在做的事情没有任何意义。你到底想要做什么......为什么? 我会支持@ErikFunkenbusch 所说的话。为什么需要手动重新创建帖子和收藏夹集合? 如果你回答了你想要做的事情,那么我们实际上可能能够帮助你解决你真正的问题.. 没有你认为你有的问题,因为你认为你必须做一件特别的事情来解决你真正的问题。 【参考方案1】:

您最好默认关闭延迟加载,而是首先指定何时加载额外数据。 EF 设置为通过在查询中使用 .Include() 函数来允许急切加载,如果您开始为各种功能打开/关闭它,延迟加载可能会变得混乱,您最好将其关闭并管理如果您觉得需要关闭它,您希望什么/何时加载数据。

请参阅https://msdn.microsoft.com/en-nz/data/jj574232.aspx 了解具体示例以及您可以急切/延迟加载数据的不同方式的细分。第一个示例展示了如何从博客中提取帖子,这与您想要实现的目标相似。

var topics = context.Topics 
                      .Include(t => t.Posts) 
                      .ToList(); 

【讨论】:

【参考方案2】:

我不知道针对这种确切情况的方法,因此我必须暂时禁用/启用延迟加载。

using(var context = new MyContext())

    context.Configuration.LazyLoadingEnabled = false;
    // do your thing using .Include() or .Load()
    context.Configuration.LazyLoadingEnabled = true;

但请注意,这是一个全局配置,因此如果在您的场景中发生这种情况,可能会出现并发问题。

【讨论】:

我反对这种方法,因为您不能再保证实体对象中存在包含的对象,因此您需要确保在尝试访问包含的对象之前始终测试空记录.在运行时打开和关闭延迟加载和代理可能会产生很多意想不到的后果,让调试很头疼。 @AllMadHare:我同意。您的解决方案要好得多。 @JeroenVannevel 这不是全局属性.. 至少在 EF 6 中没有。我想有一种方法可以全局设置它,并且该配置可能是从默认/全局克隆的,但是配置(和那个标志)是实例属性。【参考方案3】:

我不建议根据每个请求关闭延迟加载。正如 AllMadHare 建议的那样,您可以完全关闭延迟加载,但这可能会强制更改加载所有数据的方式。我建议从 Posts 中删除 virtual 关键字,以便您的课程如下所示:

public partial class Topic : Entity

    public Guid Id  get; set; 
    public string Name  get; set; 
    public DateTime CreateDate  get; set; 
    public virtual Post LastPost  get; set; 
    public virtual Category Category  get; set; 
    public IList<Post> Posts  get; set; 
    public virtual IList<TopicTag> Tags  get; set; 
    public virtual MembershipUser User  get; set; 
    public virtual IList<TopicNotification> TopicNotifications  get; set; 
    public virtual IList<Favourite> Favourites  get; set; 
    public virtual Poll Poll  get; set; 

根据此处找到的文档:https://msdn.microsoft.com/en-us/data/jj574232.aspx#lazyOffProperty 这将允许您延迟加载所有其他导航属性并在需要时急切加载帖子。

【讨论】:

【参考方案4】:

由于您使用延迟加载,因此您必须为您的类和集合属性生成代理。

用你自己的集合替换这些代理集合属性对我来说似乎很奇怪。您失去了更改跟踪,并且很可能会获得其他一些奇怪的副作用。

我建议要么使用代理/延迟加载并放弃替换集合的想法,要么停止使用代理并完全控制生成的 POCO 类。

这两种方法中哪一种最适合您的需求取决于您对实体框架的整体使用情况。

【讨论】:

以上是关于实体框架 - 停止按需延迟加载相关实体?的主要内容,如果未能解决你的问题,请参考以下文章

延迟加载 - (实体框架)底层连接意外关闭

使用 DTO 在实体框架中延迟加载

实体框架急切加载不返回数据,延迟加载有

如何使用实体框架将大量数据延迟加载到 GridView [关闭]

C# - 实体框架代码优先,延迟加载不起作用

EF6基础系列(九)---预先加载延迟加载显示加载