我正在审查一些在EF 4天内编写的代码,因为它在性能基准测试期间脱颖而出。
代码的目的是使用Entity Framework实现ICollection<MyBaseClass>
(我们现在使用的是EF 6.1)。
代码存在,因为在检索时未实现特定子类中存在的引用
public Parent
{
public virtual ICollection<MyBaseClass>() Base { get; set; }
}
从数据库中,当存储的实际类型是MyBaseClass的子类时。
示例子类:
public SubA : MyBaseClass
{
public virtual ICollection<Options> Ref1 { get; set; }
}
目前,代码执行如下操作:
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
}
}
请注意,ICollection<MyBaseClass>() Base
包含几个具体子类的混合。在ICollection
中通常有几百个物体。
有没有更有效的方法来实现Base
?
如果性能会更好(有时执行单个复杂查询,尤其是子集合包含可能实际上会产生负面影响),则不能提前说出来,但是您可以将数据库查询的数量最小化到K,其中K是数量需要额外包含的子类类型。
您需要在代表所有基本实体的LoadSubclasses
上建立IQueryable<TBase>
方法,并使用OfType
过滤器对每个子类类型执行一个查询:
private void LoadSubclasses(IQueryable<MyBaseClass> baseQuery)
{
// SubA
baseQuery.OfType<SubA>()
.Include(x => x.Ref1.Options)
.Load();
// Similar for other subclasses
}
您的样本的用法将是:
var parent = ctx.Parents.Include(p => p.Base).Where(...).Single();
LoadSubclasses(ctx.Entry(parent).Collection(p => p.Base).Query());
或更一般地说:
var parentQuery = ctx.Parents.Where(...);
var parents = parentQuery.Include(p => p.Base).ToList();
LoadSubclasses(parentQuery.SelectMany(p => p.Base));
对于EF Core 2.1或更高版本的用户,这个功能is now supported开箱即用。
2010年的要求:
当在实体框架的数据模型中具有导航属性时,除了使用OfType <>或者通过导航属性急切加载派生类型本身时,它还不具有加载该导航属性的能力。
回复2018年:
该功能是EF Core 2.1的一部分,目前正在预览中。如果您发现任何问题,请在我们的问题跟踪器中创建问题。