使用实体配置多个表的一对一关系(Configure One

编程入门 行业动态 更新时间:2024-10-27 02:30:13
使用实体配置多个表的一对一关系(Configure One-None/One Relationship with Multiple Tables using Entity)

我遇到一个表有两个One-None / One Relationships的情况。 如何使用Entity Framework Code-First实现此目的?

我见过以下链接

https://www.safaribooksonline.com/library/view/programming-entity-framework/9781449317867/ch04s07.html https://cpratt.co/0-1-to-1-relationships-in-entity-framework/ https://www.tektutorialshub.com/one-to-one-relationship-entity-framework/

基本上,据说依赖端需要有一个与主端相同的主键。 但是,如果没有确认和正确了解正在发生的事情,我会厌倦用多个One-None / One关系实现这个。 此外,我不知道如何构建语句,因为它没有传统的外键。

我还看到在表实体框架之间配置多个1到0..1的关系让我感到困惑。

请参阅下面的数据库图表的相关部分: 所以从本质上讲,不应该在没有DKImage情况下保存Player ,同样不应该在没有DKImage情况下保存Product 。

下面是模型的代码: 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; } }

DKImages

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

Player

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

DKImages

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

Products

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

Edit

I have been told that the code above is correct. If so then how do I create CRUD LINQ Statements (Or any method of constructing CRUD statements for that matter) with the above code.

最满意答案

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

如何实施?

实体框架6

在EF6那是个问题。 EF6将1:1关联实现为共享主键:子主键也是其父主键的外键。 这意味着Image.ID上应该有两个FK,一个指向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; } }

所需的映射是:

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部分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实现相比,还有一个优势:该模型允许双向导航。 可以使用“ Person和“ Product导航属性扩展Image 。

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

CREATE UNIQUE NONCLUSTERED INDEX [IX_Image_PersonID] ON [dbo].[Image]
(
    [PersonID] ASC
)
WHERE ([PersonID] IS NOT NULL)
 

这将关联转换为数据库端的真正1:1关联。 没有唯一索引,从数据库的角度来看,它将是1:n关联。

What you want here is referred to as polymorphic associations: several entities having child entities of one type. They're typically used for comments, remarks, files etc. and usually applied to 1:n associations. In your case there are polymorphic 1:1 associations. Basically these associations look like this (using a bit more generic names):

How to implement them?

Entity Framework 6

In EF6 that's problem. EF6 implements 1:1 associations as shared primary keys: the child's primary key is also a foreign key to its parent's primary key. That would mean that there should be two FKs on Image.ID , one pointing to Person.ID and another one pointing to Product.ID. Technically that's not a problem, semantically it is. Two parent entities now own the same image or, stated differently, an image should always belong to two different parents. In real life, that's nonsense.

The solution could be to reverse the references:

enter image description here

But now there's another problem. The entity that's referred to is named the principal, the other entity is dependent. In the second diagram, Image is the principal, so in order to create a Person, its image must be inserted first and then the person copies its primary key. That's counter-intuitive and most likely also impractical. It's impossible if images are optional.

Nevertheless, since in your case you want images to be required let me show how this association is mapped in EF6.

Let's take this simple model:

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

The required mapping is:

modelBuilder.Entity<Image>().HasKey(pd => pd.ImgID); modelBuilder.Entity<Person>().HasRequired(p => p.Image).WithRequiredDependent(); modelBuilder.Entity<Product>().HasRequired(p => p.Image).WithRequiredDependent();

As you see, Image has two required dependents. Perhaps that's better than two required parents, but it's still weird. Fortunately, in reality it's not a problem, because EF doesn't validate these associations. You can even insert an image without a "required" dependent. I don't know why EF doesn't validate this, but here it comes in handy. The part WithRequiredDependent might as well have been WithOptional, it doesn't make a difference for the generated data model, but at least this mapping conveys your intentions.

An alternative approach could be inheritance. If Person and Product inherit from one base class this base class could be the principal in a 1:1 association with Image. However, I think this is abusing a design pattern. People and products have nothing in common. From a design perspective there's no reason for them to be part of one inheritance tree.

Therefore, in EF6 I think the most feasible solution is to use the third alternative: separate image tables per entity.

Entity Framework Core

In EF-core 1:1 associations can be implemented the EF6 way, but it's also possible to use a separate foreign key field in the dependent entity. Doing so, the polymorphic case looks like this:

The Image class is different:

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

And the mapping:

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

Watch the nullable foreign keys. They're necessary because an image belongs to either a Person or a Product. That's one drawback of this design. Another is that you need a new foreign key field for each new entity you want to own images. Normally you want to avoid such sparse columns. There's also an advantage as compared to the EF6 implementation: this model allows bidirectional navigation. Image may be extended with Person and Product navigation properties.

EF does a pretty good job translating this into a database design. Each foreign key has a filtered unique index, for example for Person:

CREATE UNIQUE NONCLUSTERED INDEX [IX_Image_PersonID] ON [dbo].[Image]
(
    [PersonID] ASC
)
WHERE ([PersonID] IS NOT NULL)
 

This turns the association into a genuine 1:1 association on the database side. Without the unique index it would be a 1:n association from the database's perspective.

更多推荐

public,set,Player,电脑培训,计算机培训,IT培训"/> <meta name="description

本文发布于:2023-07-19 21:49:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1187570.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:多个   实体   关系   Configure

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!