实现一对零或一对一关系和一对多关系 Class

Implementing a One-to-Zero-or-One Relationship and a One-to-Many Relationship of the Same Class

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

我希望 MainAboutPage 是可选的(一对零或一对一)并且 AboutSubPages 显然是一对多。

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

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; }
}

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

Unable to determine the principal end of the relationship. Multiple added entities may have the same primary key.


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

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

我得到:

Unable to determine the principal end of the 'Wesbite_AboutSubPages' relationship. Multiple added entities may have the same primary key.


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

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

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

我得到:

Unable to determine the principal end of the 'Website_MainAboutPage' relationship. Multiple added entities may have the same primary key.


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

        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: : Multiplicity is not valid in Role 'Wesbite_MainAboutPage_Target' in relationship 'Website_MainAboutPage'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.


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

我的大脑被腌制了,如果我遗漏了一些明显的东西,请原谅。我真的很感激一些关于如何按照我的意愿进行设置的指示。

提前致谢。

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

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

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

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

更好的方法可能是只维护一个 AboutSubPages 集合并向 SubPage 实体添加一个 PageOrder 数字唯一索引。例如,"main" 子页面将是具有最低 PageOrder 的子页面。

即select 一个网站的详细信息,它 "main" 关于页面:

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

... 举个例子。这确保我们可以访问主页,同时确保网站认为唯一的页面是分配给它的页面。可以在网站实体上将未映射的 属性 设置为嵌入式集合中的 return "MainAboutPage",但是我不建议使用未映射的属性,因为它们很容易滑入 Linq表达式和 EF 将引发异常或执行过早执行 (EF Core) 来解决它们。