简体   繁体   中英

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... :/

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(); 

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.

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.

Since you are using lazy loading, you must have proxies generated for your classes and collection properties.

Replacing these proxy collection properties with your own collections seems pretty weird design to me. You loose change tracking and most probably gain a couple of other strange side effects.

I would recommend to either use proxies/lazy loading and give up the idea of replacing the collections, or stand back from using proxies and gain full control on the generated POCO classes.

Which of both approaches suits best for your needs depends on your overall usage of the entity framework.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM