首页 > 解决方案 > 实现同一类的一对零或一关系和一对多关系

问题描述

我在映射以下类时遇到问题。

我想MainAboutPage成为可选的(一对零或一)并且AboutSubPages显然是一对多的。

理想情况下,我想将WebsiteId属性保留在WebsitePage课堂上。

public class Website
{
    public int Id { get; set; }

    public virtual WebsitePage MainAboutPage { get; set; }

    public ICollection<WebsitePage> AboutSubPages { get; set; }

}


public class WebsitePage
{
    public int Id { get; set; }

    public int WebsiteId { get; set; }

    public virtual Website Website { get; set; }
}

当我不使用流利的映射时,我得到

无法确定关系的主体端。多个添加的实体可能具有相同的主键。


当我使用这个流畅的映射时:

        modelBuilder.Entity<Wesbite>()
            .HasMany(x => x.AboutSubPages)
            .WithRequired(x => x.Website)
            .HasForeignKey(x => x.WebsiteId);

我得到:

无法确定“Wesbite_AboutSubPages”关系的主体端。多个添加的实体可能具有相同的主键。


当我使用这个流畅的映射时:

        modelBuilder.Entity<Website>()
           .HasOptional(x => x.MainAboutPage)
           .WithRequired();

        modelBuilder.Entity<Wesbite>()
            .HasMany(x => x.AboutSubPages)
            .WithRequired(x => x.Website)
            .HasForeignKey(x => x.WebsiteId);

我得到:

无法确定“Website_MainAboutPage”关系的主体端。多个添加的实体可能具有相同的主键。


当我使用这个流畅的映射时:

        modelBuilder.Entity<Website>()
           .HasOptional(x => x.MainAboutPage)
           .WithRequired(x => x.Website);

        modelBuilder.Entity<Wesbite>()
            .HasMany(x => x.AboutSubPages)
            .WithRequired(x => x.Website)
            .HasForeignKey(x => x.WebsiteId);

我得到:

Wesbite_MainAboutPage_Target::多重性在关系“Website_MainAboutPage”中的角色“Wesbite_MainAboutPage_Target”中无效。因为从属角色属性不是关键属性,所以从属角色的多重性的上限必须是“*”。


我一直在无休止地阅读 MS 的配置示例:https ://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx和https://www.entityframeworktutorial .net/code-first/configure-one-to-many-relationship-in-code-first.aspx

我的大脑被腌制了,如果我遗漏了一些明显的东西,请原谅我。我真的很感激一些关于我想要的设置的指示。

提前致谢。

标签: asp.net-mvcentity-frameworkone-to-manyef-fluent-apifluent-interface

解决方案


我相信问题在于您没有足够的信息让 EF 区分关于网站的 AboutSubPages 和 MainAboutPage 引用。要在网站上为 MainAboutPage 建立 1..0/1 关系,您需要在网站表中声明 MainAboutPageId。

modelBuilder.Entity<Website>()
   .HasOptional(x => x.MainAboutPage)
   .WithRequired(x => x.Website)
   .HasForeignKey(x => x.MainAboutPageId);

或者,您可以选择使用影子属性 (EF Core) 或Map.MapKey(EF6) 来映射关系,而无需在实体中公开 FK。(推荐的)

具有 1..0/1 加上 1..many 与相同相关实体的实体关系的警告是,无法强制 MainAboutPage 实际上属于子集合。因为 1..0/1 依赖于从网页到子页面的 FK,所以没有强制要求子页面必须指向同一个网站。EF 和 Database 非常乐意让 WebSite ID #1 指向具有 WebSite ID #2 的子页面。

更好的方法可能是只查看维护 AboutSubPages 集合并将 PageOrder 数字唯一索引添加到 SubPage 实体。例如,“主”子页面将是具有最低 PageOrder 的页面。

即选择一个网站的详细信息,它是关于页面的“主要”:

var websites = context.Websites
    .Select(x => new WebsiteSummaryViewModel
    {
        Name = x.Name,
        AboutPageURL = x.AboutSubPages
            .OrderBy(a => a.PageOrder)
            .Select(a => a.Url)
            .FirstOrDefault()
    }).ToList();

... 举个例子。这确保了我们可以访问主页,同时确保网站考虑的唯一页面是分配给它的页面。可以在网站实体上设置未映射的属性以从嵌入式集合中返回“MainAboutPage”,但是我不建议使用未映射的属性,因为它们很容易滑入 Linq 表达式,并且 EF 将抛出异常或执行过早执行(EF Core)来解决这些问题。


推荐阅读