使用实体配置与多个表的一对一/一对关系

Jam*_*oo0 4 c# entity-framework

我所处的情况是一张表有两个一对一/一关系。如何使用实体框架代码优先来实现这一点?

我看过以下链接

从本质上讲,从属端需要有一个与主体端相同的主键。但我厌倦了在没有确认和正确了解正在发生的事情的情况下通过多个一对一/一关系来实现这一点。此外,我不确定如何构建语句,因为它没有传统的外键。

我还看到了在表实体框架之间配置多个 1 到 0..1 关系,这让我困惑得无法辨认。

请参阅下面我的数据库图的相关部分: 所以本质上,aPlayer不应该在没有 a 的情况下保存DKImage,同样,aProduct不应该在没有 a 的情况下保存DKImage

下面是模型的代码:Players,,Products我知道这是不正确的,我只是这样实现的,这样我就可以生成数据库并显示DKImages图表)

玩家

public enum Positions { PG, SG, SF, PF, C }

public class Player
{
    [Key]
    [ForeignKey("Images")]
    public int PlayerID { get; set; }

    [Required]
    public string PlayerName { get; set; }

    [Required]
    public string PlayerLastName { get; set; }

    [Required]
    public int PlayerAge { get; set; }

    [Required]
    public Positions Position { get; set; }

    [Required]
    public bool Starter { get; set; }

    [Required]
    [Display(Name = "Active / Not Active")]
    public bool Status { get; set; }

    //Foreign Keys
    public int PlayerStatsID { get; set; }

    //Navigation Properties
    [ForeignKey("PlayerStatsID")]
    public virtual IQueryable<PlayerStats> PlayerStats { get; set; }
    public virtual DKImages Images { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

DK图像

public class DKImages
{
    [Key]
    public int ImageID { get; set; }
    [Required]
    public string ImageURL { get; set; }
    [Required]
    public DateTime DateUploaded { get; set; }

    //Foreign Keys
    [Required]
    public int CategoryID { get; set; }

    //Navigation Properties
    public virtual Products Products { get; set; }
    public virtual Category Category { get; set; }
    public virtual Player Player { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

产品

public class Products
{
    [ForeignKey("Images")]
    [Key]
    public int ProductID { get; set; }
    [Required]
    public string ProductName { get; set; }
    [Required]
    public DateTime DateAdded { get; set; }

    //Foreign Keys
    [Required]
    public int ProductTypeID { get; set; }

    //Navigation Properties
    [ForeignKey("ProductTypeID")]
    public virtual ProductType ProductType { get; set; }
    public virtual DKImages Images { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

编辑

我被告知上面的代码是正确的。如果是这样,那么我如何使用上面的代码创建 CRUD LINQ 语句(或任何构造 CRUD 语句的方法)。

Ger*_*old 5

这里您想要的是多态关联:具有一种类型的子实体的多个实体。它们通常用于评论、评论、文件等,并且通常应用于 1:n 关联。在您的情况下,存在多态 1:1 关联。基本上这些关联看起来像这样(使用更通用的名称):

如何实施它们?

实体框架6

在 EF6 中这是个问题。EF6 将 1:1 关联实现为共享主键:子级主键也是其父级主键的外键。这意味着应该有两个 FK 处于打开状态Image.ID,一个指向Person.ID,另一个指向Product.ID。从技术上来说这不是问题,从语义上来说这是问题。两个父实体现在拥有相同的图像,或者换句话说,图像应该始终属于两个不同的父实体。在现实生活中,这是无稽之谈。

解决方案可能是反转引用:

在此输入图像描述

但现在还有另一个问题。所引用的实体称为主体,另一个实体是从属实体。在第二个图中,Image是主体,因此为了创建Person,必须首先插入其图像,然后复制其主键。这是违反直觉的,而且很可能也是不切实际的。如果图像是可选的,这是不可能的。

不过,由于在您的情况下您希望需要图像,让我展示一下如何在 EF6 中映射此关联。

让我们采用这个简单的模型:

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual Image Image { get; set; }
}

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual Image Image { get; set; }
}

public class Image
{
    public int ImgID { get; set; } // Named for distinction
    public string Url { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

所需的映射是:

modelBuilder.Entity<Image>().HasKey(pd => pd.ImgID);
modelBuilder.Entity<Person>().HasRequired(p => p.Image).WithRequiredDependent();
modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithRequiredDependent();
Run Code Online (Sandbox Code Playgroud)

如您所见,Image有两个必需的家属。也许这比需要两个父母要好,但这仍然很奇怪。幸运的是,实际上这不是问题,因为 EF 不会验证这些关联。您甚至可以插入没有“必需”依赖项的图像。我不知道为什么 EF 不验证这一点,但它在这里派上了用场。这部分WithRequiredDependent也可能是WithOptional,它对生成的数据模型没有影响,但至少这个映射传达了您的意图。

另一种方法是继承。如果从一个基类Person继承Product,则该基类可能是与 1:1 关联中的主体Image。然而,我认为这是滥用设计模式。人和产品没有任何共同点。从设计的角度来看,它们没有理由成为一个继承树的一部分。

因此,在EF6中我认为最可行的解决方案是使用第三种替代方案:每个实体单独的图像表

实体框架核心

在 EF-core 1:1 关联中,可以通过 EF6 方式实现,但也可以在依赖实体中使用单独的外键字段。这样做,多态情况如下所示:

班级Image不同:

public class Image
{
    public Image()
    { }
    public int ImgID { get; set; }
    public int? PersonID { get; set; }
    public int? ProductID { get; set; }
    public string Url { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和映射:

modelBuilder.Entity<Person>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Person>()
    .HasOne(p => p.Image)
    .WithOne()
    .HasForeignKey<Image>(p => p.PersonID);
modelBuilder.Entity<Product>().Property(p => p.ID).UseSqlServerIdentityColumn();
modelBuilder.Entity<Product>()
    .HasOne(p => p.Image)
    .WithOne()
    .HasForeignKey<Image>(p => p.ProductID);
modelBuilder.Entity<Image>().HasKey(p => p.ImgID);
Run Code Online (Sandbox Code Playgroud)

观察可为空的外键。它们是必要的,因为图像属于 aPerson或 a Product。这是这种设计的一个缺点。另一个是您想要拥有图像的每个新实体都需要一个新的外键字段。通常您希望避免这种稀疏的列。与 EF6 实现相比,还有一个优点:该模型允许双向导航。Image可以通过导航属性进行Person扩展Product

EF 在将其转化为数据库设计方面做得非常好。每个外键都有一个经过过滤的唯一索引,例如Person

public class Person
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual Image Image { get; set; }
}

public class Product
{
    public int ID { get; set; }
    public string Name { get; set; }
    public virtual Image Image { get; set; }
}

public class Image
{
    public int ImgID { get; set; } // Named for distinction
    public string Url { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这使得该关联在数据库端变成了真正的1:1关联。如果没有唯一索引,从数据库的角度来看,这将是 1:n 关联。