Materializing an ICollection structure containing subclasses

半世苍凉 提交于 2019-12-31 05:20:49

问题


I'm reviewing some code that was written in the EF 4 days because it stands out during performance benchmarking.

The purpose of the code is to materialize an ICollection<MyBaseClass> using Entity Framework (we're now on EF 6.1).

The code exists because references present in specific subclasses aren't materialized when retrieving

public Parent
{
    public virtual ICollection<MyBaseClass>() Base { get; set; }
}

from the database, when the actual types stored are subclasses of MyBaseClass.

Example subclass:

public SubA : MyBaseClass
{
    public virtual ICollection<Options> Ref1 { get; set; }
}

Currently, the code does something like this:

var parent = ctx.Parents.Include(p => p.Base).Where(...).Single();
LoadSubclasses(parent.Base);
...
private void LoadSubclasses(IEnumerable<MyBaseClass> myBase)
{
    foreach (var my in myBase)
    {
        if (my is SubA)
        {
            this.Entry(my).Reference("Ref1").Load();
            this.Entry((SubA)my).Ref1).Collection("Options").Load();
        }
        else... // Similar for other subclasses
    }
}

Note that ICollection<MyBaseClass>() Base contains a mix of several concrete subclasses. There are generally a few hundred objects in the ICollection.

Is there a more efficient way to materialize Base?


回答1:


It cannot be said in advance if the performance will be better (sometimes executing a single complex query, especially with sub collection includes may have actually negative impact), but you can minimize the number of database queries to K, where K is the number of subclass types that need additional includes.

You need to base the LoadSubclasses method on IQueryable<TBase> representing all base entities, and execute one query per each subclass type using OfType filter:

private void LoadSubclasses(IQueryable<MyBaseClass> baseQuery)
{
    // SubA
    baseQuery.OfType<SubA>()
        .Include(x => x.Ref1.Options)
        .Load();
   // Similar for other subclasses
}

The usage with your sample would be:

var parent = ctx.Parents.Include(p => p.Base).Where(...).Single();
LoadSubclasses(ctx.Entry(parent).Collection(p => p.Base).Query());

or more generally:

var parentQuery = ctx.Parents.Where(...);
var parents = parentQuery.Include(p => p.Base).ToList();
LoadSubclasses(parentQuery.SelectMany(p => p.Base));



回答2:


For those on EF Core 2.1 or later, this feature is now supported out-of-the-box.

Request from 2010:

When in an data model for entity framework has a navigation property it is not posseble to eager load that navigation property besides when using OfType<> or when eager loading the derived type itself by a navigation property.

Response from 2018:

The feature is part of EF Core 2.1, which is currently in preview. Please create issues in our issue tracker if you find any problems.



来源:https://stackoverflow.com/questions/45787939/materializing-an-icollection-structure-containing-subclasses

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!