Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework - Stop Lazy Loading Related Entities On Demand?

I have Entity Framework set up and it works fine most of the time I need it. I have a structure like so

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; }
}

As you can see I have a number of related entities which are lists. These are mapped as standard and are lazy loaded so I can call Topic.Posts or Topic.TopicNotifications etc... (Mappings below)

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");
                });

This is all an well. But on a couple of occasions I have the need to manually populate Topic.Posts and Topic.Favorites.

But if I try and set Topic.Posts = SomeCollection it triggers the lazy load and loads all the posts first, and then lets me set my collection so I get two sets of sql executed (The first I don't want)

Is there anyway, to switch off the lazy loading manually on demand for just when I want to set the collection manually?

Hope that makes sense... :/

like image 531
YodasMyDad Avatar asked Apr 15 '15 05:04

YodasMyDad


People also ask

How can you disable lazy loading for all entities?

We can disable lazy loading for a particular entity or a context. To turn off lazy loading for a particular property, do not make it virtual. To turn off lazy loading for all entities in the context, set its configuration property to false.

Does Entity Framework support lazy loading?

Entity Framework supports three ways to load related data - eager loading, lazy loading and explicit loading.

Which entities allow lazy loading?

Yes, lazy loading is enabled in the Entity Framework ORM too, it is on by default in Entity Framework, so if you want to enable lazy loading in Entity Framework, you don't need to do anything.


3 Answers

You would be better to turn off lazy loading by default and instead specify when you want to load the extra data in the first place. EF is set up to allow Eager loading by using the .Include() function on your query, with lazy loading it can get messy if you start turning it on/off for various functions, you're better to just turn it off and manage what/when you want data loaded if you're feeling a need to turn it off.

See https://msdn.microsoft.com/en-nz/data/jj574232.aspx for specific examples and a breakdown of the different ways you can eager/lazy load data. The first example shows how you could pull through posts off a blog, which is similar to what you are wanting to acheive.

var topics = context.Topics 
                      .Include(t => t.Posts) 
                      .ToList(); 
like image 89
AllMadHare Avatar answered Oct 22 '22 17:10

AllMadHare


I am not aware of an approach targeted to this exact scenario so I'd have to go with a temporary disable/enable of lazy loading.

using(var context = new MyContext())
{
    context.Configuration.LazyLoadingEnabled = false;
    // do your thing using .Include() or .Load()
    context.Configuration.LazyLoadingEnabled = true;
}

Note however that this is a global configuration so there might be a concurrency problem if that happens in your scenario.

like image 23
Jeroen Vannevel Avatar answered Oct 22 '22 17:10

Jeroen Vannevel


I would not recommend turning off lazing loading on a per request basis. As AllMadHare suggests, you could turn off lazy loading entirely, but that could force changes to how you load all data. I would recommend removing the virtual keyword from Posts so that your class looks like this:

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; }
}

Per the documentation found here: https://msdn.microsoft.com/en-us/data/jj574232.aspx#lazyOffProperty this will allow you to lazy load all other navigation properties and eager load posts if you need to.

like image 32
ajliptak Avatar answered Oct 22 '22 17:10

ajliptak