我遇到一个表有两个One-None / One Relationships的情况。如何使用Entity Framework Code-First实现此目的?
我见过以下链接
基本上说,从属端需要有一个与主端相同的主键。但是我厌倦了用多个One-None / One关系实现这个,而没有确认和正确知道发生了什么。此外,我不知道如何构建语句,因为它没有传统的外键。
我也看到Configuring multiple 1 to 0..1 relationships between tables entity framework让我感到困惑。
请参阅下面的数据库图表的相关部分:
因此,基本上,如果没有Player
,则不应保存DKImage
,同样,如果没有Product
,则不应保存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; }
}
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; }
}
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; }
}
修改
我被告知上面的代码是正确的。如果是,那么如何使用上面的代码创建CRUD LINQ语句(或者为此构建CRUD语句的任何方法)。
答案 0 :(得分:1)
这里你想要的是polymorphic associations:几个拥有一种类型的子实体的实体。它们通常用于评论,备注,文件等,通常应用于1:n关联。在您的情况下,有多态1:1关联。基本上这些关联看起来像这样(使用更通用的名称):
如何实施?
在EF6中这是个问题。 EF6将1:1关联实现为共享主键:子主键也是其父主键的外键。这意味着Image.ID
上应该有两个FK,一个指向Person.ID
,另一个指向Product.ID
。从技术上讲,这不是问题,从语义上讲也是如此。两个父实体现在拥有相同的图像,或者换句话说,图像应始终属于两个不同的父项。在现实生活中,这是无稽之谈。
解决方案可能是推翻参考文献:
但现在又出现了另一个问题。引用到的实体名为 principal ,另一个实体是依赖。在第二个图中,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; }
}
所需的映射是:
modelBuilder.Entity<Image>().HasKey(pd => pd.ImgID);
modelBuilder.Entity<Person>().HasRequired(p => p.Image).WithRequiredDependent();
modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithRequiredDependent();
如您所见,Image
有两个必需的依赖项。也许这比两个必需的父母要好,但它仍然很奇怪。幸运的是,实际上这不是问题,因为EF不会验证这些关联。您甚至可以插入没有“必需”依赖的图像。我不知道为什么EF不会对此进行验证,但在这里它会派上用场。部分WithRequiredDependent
也可能是WithOptional
,它对生成的数据模型没有任何影响,但至少这种映射传达了您的意图。
另一种方法可能是继承。如果Person
和Product
从一个基类继承,则此基类可以是与Image
的1:1关联中的主体。但是,我认为这是滥用设计模式。人与产品没有任何共同之处。从设计的角度来看,它们没有理由成为一个继承树的一部分。
因此,在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; }
}
映射:
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);
观看可以为空的外键。它们是必需的,因为图片属于Person
或Product
。这是这种设计的一个缺点。另一个是您需要为要拥有图像的每个新实体创建一个新的外键字段。通常你想避免这种稀疏列。与EF6实现相比,还有一个优势:该模型允许双向导航。 Image
可以使用Person
和Product
导航属性进行扩展。
EF在将其转化为数据库设计方面做得非常出色。每个外键都有一个过滤的唯一索引,例如Person
:
CREATE UNIQUE NONCLUSTERED INDEX [IX_Image_PersonID] ON [dbo].[Image]
(
[PersonID] ASC
)
WHERE ([PersonID] IS NOT NULL)
这将关联转换为数据库端的真正1:1关联。没有唯一索引,从数据库的角度来看,它将是1:n关联。
答案 1 :(得分:0)
您的播放器表中的例子是:
public class Player
{
// All the rest you already coded
[Required]
public int ImageID
[ForeignKey("ImageID")]
public virtual DKImage DKImage {get;set;}
}
这会强制玩家拥有DKImage,但正如评论中所述,这会产生一对多的关系。
另一种方法是将所有Player字段放入DKImage表中,如果没有与此DKImage关联的播放器,这些字段将为null。
编辑1到1..0
Ivan Stoev的链接对如何实现这一目标有了一些非常有趣的见解:
您似乎需要在课程中添加更多代码:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<DKImage>().HasOptional(t => t.Player).WithRequired();
}
如果教程是正确的,这将读作:
&#34; DKImage实体与一个Player对象具有可选关联,但Player实体&#34;需要此关联。
我还没有测试过。