C# 卷影属性以访问具有表/层次结构继承模型的相关实体

C# 卷影属性以访问具有表/层次结构继承模型的相关实体,c#,linq,asp.net-core-mvc,entity-framework-core,navigation-properties,C#,Linq,Asp.net Core Mvc,Entity Framework Core,Navigation Properties,我正在用ASP.NETCore为一个网站构建一个菜单系统。让我们假设我有两个数据库表,一个用于页面,一个用于文章,尽管它们是不同的实体才是真正重要的。它们都有一个名称和永久链接属性 在我的菜单中(我也希望存储在数据库中),我希望引用每个实体的名称和永久链接。我设计了一个简单的菜单类/模型结构,如下所示: 抽象菜单项 public abstract class MenuItem { [Key] public int MenuItemId { get; set; } publ

我正在用ASP.NETCore为一个网站构建一个菜单系统。让我们假设我有两个数据库表,一个用于
页面
,一个用于
文章
,尽管它们是不同的实体才是真正重要的。它们都有一个
名称
永久链接
属性

在我的菜单中(我也希望存储在数据库中),我希望引用每个实体的
名称
永久链接
。我设计了一个简单的菜单类/模型结构,如下所示:

抽象菜单项

public abstract class MenuItem
{
    [Key]
    public int MenuItemId { get; set; }
    public int MenuPosition { get; set; }
    public abstract string Name { get; }
    public abstract string Permalink { get; }
}
public class ArticleMenuItem : MenuItem
{
    public ArticleMenuItem() {}
    public ArticleMenuItem(Article article)
    {
        Article = article;
    }

    public string Name => Article.Name;
    public string Permalink => Article.Permalink;

    [Required]
    public int ArticleId { get; set; }
    [ForeignKey("ArticleId")]
    public Article Article { get; set; }
}
public class PageMenuItem : MenuItem
{
    public PageMenuItem() {}
    public PageMenuItem(Page page)
    {
        Page = page;
    }

    public string Name => Page.Name;
    public string Permalink => Page.Permalink;

    [Required]
    public int PageId { get; set; }
    [ForeignKey("PageId")]
    public Page Page{ get; set; }
}
具体文章菜单项

public abstract class MenuItem
{
    [Key]
    public int MenuItemId { get; set; }
    public int MenuPosition { get; set; }
    public abstract string Name { get; }
    public abstract string Permalink { get; }
}
public class ArticleMenuItem : MenuItem
{
    public ArticleMenuItem() {}
    public ArticleMenuItem(Article article)
    {
        Article = article;
    }

    public string Name => Article.Name;
    public string Permalink => Article.Permalink;

    [Required]
    public int ArticleId { get; set; }
    [ForeignKey("ArticleId")]
    public Article Article { get; set; }
}
public class PageMenuItem : MenuItem
{
    public PageMenuItem() {}
    public PageMenuItem(Page page)
    {
        Page = page;
    }

    public string Name => Page.Name;
    public string Permalink => Page.Permalink;

    [Required]
    public int PageId { get; set; }
    [ForeignKey("PageId")]
    public Page Page{ get; set; }
}
具体页面菜单项

public abstract class MenuItem
{
    [Key]
    public int MenuItemId { get; set; }
    public int MenuPosition { get; set; }
    public abstract string Name { get; }
    public abstract string Permalink { get; }
}
public class ArticleMenuItem : MenuItem
{
    public ArticleMenuItem() {}
    public ArticleMenuItem(Article article)
    {
        Article = article;
    }

    public string Name => Article.Name;
    public string Permalink => Article.Permalink;

    [Required]
    public int ArticleId { get; set; }
    [ForeignKey("ArticleId")]
    public Article Article { get; set; }
}
public class PageMenuItem : MenuItem
{
    public PageMenuItem() {}
    public PageMenuItem(Page page)
    {
        Page = page;
    }

    public string Name => Page.Name;
    public string Permalink => Page.Permalink;

    [Required]
    public int PageId { get; set; }
    [ForeignKey("PageId")]
    public Page Page{ get; set; }
}
然后,我为相关的
DbContext
覆盖
onModelCreating(ModelBuilder-ModelBuilder)
,因为我不想使单个
DbSet的
可用:

modelBuilder.Entity<PageMenuItem>();
modelBuilder.Entity<ArticleMenuItem>();
不幸的是,这导致了一个
空对象异常
,然后我记得EF Core不支持延迟加载。因此,当我在存储库中获得菜单项时,我想急切地加载阴影属性,特别是相关的实体

访问阴影属性有多种方法。我采用的第一种更新存储库方法如下所示:

public IEnumerable<MenuItem> GetAllMenuItems() => _context.MenuItems
    .Include(item => context.Entry(item).Property("Page").CurrentValue)
    .Include(item => context.Entry(item).Property("Article").CurrentValue)
public IEnumerable<MenuItem> GetAllMenuItems()
{
    var menuItems = _context.MenuItems.ToList();
    _context.MenuItems.OfType<ArticleMenuItem>().Include(e => e.Article).Load();
    _context.MenuItems.OfType<PageMenuItem>().Include(e => e.Page).Load();
    return menuItems;
}
结果:

InvalidOperationException:属性表达式'item=>property(item,“Page”)'无效。表达式应表示属性访问:“t=>t.MyProperty”


我想知道是否可以在继承模型中使用阴影属性进行导航?如果是,我如何包括相关实体,以便在我的具体
MenuItem
类中可以访问它?e、 g.对于
publicstringname=>Page.Name

不幸的是,目前没有用于快速加载派生类属性的语法(请注意,它们与影子属性不同)。这加上缺少延迟加载,使得显式加载成为唯一的选择。例如,查看如何将其用于单个项目,对于项目集合,恐怕您必须将结果具体化为一个列表,然后使用具体类型的显式加载并依赖EF导航属性修复

例如,它可以是这样的:

public IEnumerable<MenuItem> GetAllMenuItems() => _context.MenuItems
    .Include(item => context.Entry(item).Property("Page").CurrentValue)
    .Include(item => context.Entry(item).Property("Article").CurrentValue)
public IEnumerable<MenuItem> GetAllMenuItems()
{
    var menuItems = _context.MenuItems.ToList();
    _context.MenuItems.OfType<ArticleMenuItem>().Include(e => e.Article).Load();
    _context.MenuItems.OfType<PageMenuItem>().Include(e => e.Page).Load();
    return menuItems;
}
public IEnumerable GetAllMenuItems()
{
var menuItems=_context.menuItems.ToList();
_context.MenuItems.OfType().Include(e=>e.Article.Load();
_context.MenuItems.OfType().Include(e=>e.Page.Load();
返回菜单项;
}
另一种解决方法(我是否说过只有一种)是使用手动联合查询,这基本上扼杀了TPH的想法:

public IEnumerable<MenuItem> GetAllMenuItems() =>
    _context.MenuItems.OfType<ArticleMenuItem>().Include(e => e.Article)
    .AsEnumerable() // to avoid runtime exception (EF Core bug)
    .Concat<MenuItem>(
    _context.MenuItems.OfType<PageMenuItem>().Include(e => e.Page));
public IEnumerable GetAllMenuItems()=>
_context.MenuItems.OfType().Include(e=>e.Article)
.AsEnumerable()//避免运行时异常(EF核心错误)
康卡特先生(
_context.MenuItems.OfType().Include(e=>e.Page));

好的,我对术语的误解。这有助于我了解一开始出了什么问题。我来看看你的建议。