使用实体

时间:2018-05-29 10:43:26

标签: c# entity-framework

我遇到一个表有两个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

下面是模型的代码:PlayersProductsDKImages我知道它不正确,我只是这样实现它所以我可以生成数据库和显示图

播放器

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

修改

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

2 个答案:

答案 0 :(得分:1)

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

如何实施?

实体框架6

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

解决方案可能是推翻参考文献:

enter image description here

但现在又出现了另一个问题。引用的实体名为 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,它对生成的数据模型没有任何影响,但至少这种映射传达了您的意图。

另一种方法可能是继承。如果PersonProduct从一个基类继承,则此基类可以是与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);

观看可以为空的外键。它们是必需的,因为图片属于PersonProduct。这是这种设计的一个缺点。另一个是您需要为要拥有图像的每个新实体创建一个新的外键字段。通常你想避免这种稀疏列。与EF6实现相比,还有一个优势:该模型允许双向导航。 Image可以使用PersonProduct导航属性进行扩展。

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的链接对如何实现这一目标有了一些非常有趣的见解:

https://weblogs.asp.net/manavi/associations-in-ef-4-1-code-first-part-3-shared-primary-key-associations

您似乎需要在课程中添加更多代码:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<DKImage>().HasOptional(t => t.Player).WithRequired();

}

如果教程是正确的,这将读作:

&#34; DKImage实体与一个Player对象具有可选关联,但Player实体&#34;需要此关联。

我还没有测试过。