EF代码优先-预先加载和过滤子类属性(继承)

本文关键字:属性 子类 过滤 继承 加载 代码 EF | 更新日期: 2023-09-27 18:04:19

我有以下类(过于简化):

public class Person
{
    public int ID { get; set; }
}
public class Content
{
    public int ID { get; set; }
}
public class Image : Content
{
    public bool Private { get; set; }
    public Person Author { get; set; }
}
public class Tag
{
    public int ID { get; set; }
    public Content Content { get; set; }
    public Person Person { get; set; }
}

我想得到所有的Tags,其中ContentImage, Image不是Private(同时急切地加载Image的属性)。尝试这样做,但不工作的例子:

var tags = context.Tags
    .Include("Content.Author")
    .Include("Person")
    .Where(t => !((Image)t.Content).Private);

得到以下错误:

无法将"内容"类型强制转换为"图像"类型。LINQ to Entities只支持转换EDM基本类型或枚举类型。

删除Where子句后:

指定的包含路径无效。EntityType 'Content'没有声明名为'Author'的导航属性。

我需要什么样的查询和/或模型模式更改才能完成这种方法?

EF代码优先-预先加载和过滤子类属性(继承)

您可以在Where子句中按照以下方式编写过滤器:

.Where(t => t.Content is Image && !(t.Content as Image).Private)

然而,更大的问题是Include部分。Author属性只存在于派生类型Image,但Include将尝试加载基本类型Content(没有Author属性),因为这是Tag中导航属性Content的类型。你不能在这里使用Include

您可以尝试将查询重写为投影:

var tags = context.Tags
    .Where(t => t.Content is Image && !(t.Content as Image).Private)
    .Select(t => new
    {
        Tag = t,
        Image = t.Content as Image, // possibly this line is not needed
        Author = (t.Content as Image).Author,
        Person = t.Person
    })
    .AsEnumerable()
    .Select(x => x.Tag)
    .ToList();

只要您不禁用更改跟踪(例如,使用AsNoTracking) EF应该自动将对象图放在一起,以便加载的标签具有填充的Content, Content.AuthorPerson属性(就好像您已经使用Include加载了导航属性)。

顺便说一句:包含派生类型的导航属性的功能是在UserVoice上请求的。这与您的情况不完全相同,但在评论部分是一个请求,甚至适合您的场景。

尝试将类定义更改为…

class Person
{
  public int ID { get; set; }
}
class Content
{
  public int ID { get; set; }
}
class Image : Content
{
  public bool IsPrivate { get; set; }   
  public virtual Person Author { get; set; }
}
class Tag
{
public int ID { get; set; }
public Content Content { get; set; }
public Person Person { get; set; }
}

Private似乎不是个好名字,因为它与public或Private声明冲突。