如何在 EF 中创建从两个表到一个源的一对一映射?

Posted

技术标签:

【中文标题】如何在 EF 中创建从两个表到一个源的一对一映射?【英文标题】:How to create one-to-one mapping in EF from two tables to one source? 【发布时间】:2022-01-06 04:02:15 【问题描述】:

我想要 2 个模型,例如“人”和“动物”。两者都在 DB (postrge) 中建模,带有 image_binary_id 列。

    public class Person
    
        public long Id  get; set; 
        public string Name  get; set; 
        public ImageBinary ImageBinary  get; set; 
    

    public class Animal
    
        public long Id  get; set; 
        public string Name  get; set; 
        public ImageBinary ImageBinary  get; set; 
    

现在,我希望图像数据表使用模型为两个对象“提供”图像:

    public class ImageBinary
    
        public long Id  get; set; 
        public string Name  get; set; 
        public byte[] Data  get; set; 
    

在数据库中,我有外键连接“person.image_binary_id => image_binary.id”

如何在代码中定义映射,以便在 1 个事务中正确插入,如下所示:

var image = new ImageBinary(...);
var person = new Person() 

    ...,
    ImageBinary = image

PersonRepository.AddPerson(person);

我尝试像这样映射person,但这会导致插入时 image_binary_id 为null

entity.HasOne(x => x.ImageBinary)
   .WithOne()
   .HasForeignKey("ImageBinary", "Id");

我知道如果“ImageBinary”表将包含person\animal 列,这可能会解决它,因为它将返回“导航”(将参数添加到WithOne())-但我不想添加更多列(除非我必须...)。

我曾考虑过添加联结表(person_id、image_binary_id),但这似乎很难维护,因为我还必须为所有未来的模型添加一个。

将 .NET 5 与 EF Core 版本 5 结合使用。

欢迎任何想法和解决方案。

【问题讨论】:

除非我必须这样做 -- 你最好这样做。我一直发现这是解决这种多态关联的最佳方案。甚至为每个实体创建一个图像表。真的没有听起来那么糟糕。 好的,为了澄清,您建议在ImageBinary 表中添加列,其中包含引用对象的类型?在这种情况下,额外的列将是 entity_type,其值选项为 person \ animal - 因此来自该表的查询将基于 Id + 类型? 不,我会为每个实体添加一个外键,这样您就有了数据完整性。 【参考方案1】:

这是您尝试做的代码的第一个实现。

public class Person
    
        [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id  get; set; 
        public string Name  get; set; 
        
        public long ImageBinRef  get; set; 
        [ForeignKey("ImageBinRef")]
        //or [ForeignKey(nameof(ImageBinRef))] I like this version better
        public ImageBinary ImageBinary  get; set; 
    

这是我在这里所做的一些解释

Key 属性告诉 Ef Id 是主键

DatabaseGenerated(DatabaseGeneratedOption.Identity) 告诉 ef 它是 自动递增

ForeignKey 属性将 fk 约束和索引添加到 ImageBinRef 我在回答中为您定义的属性,因此您尝试运行的示例现在可以工作

这里的另一件重要的事情是您的 ImageBinary 类的主键是 long 类型,它不能为空并且不接受 null 作为值。 这很重要,因为现在当您删除人员记录时,如果您不希望删除该记录,它还将删除与该记录相关的 ImageBinary 记录(我不知道这是否是您想要的),只需像这样声明属性

public long? ImageBinRef  get; set;  //Notice the question mark
    

问号表示它是一个可以为空的类型,所以现在当您删除人员记录时,它不会删除引用的图像记录..

请注意,默认情况下字符串可以为空,因此您无需担心:

假设你有 COUNTRIES 表和 PERSON 表

public class PERSON
   
 [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
 public int PersonRowid get;set:
 public string PersonName get;set:

 public string PersonCountryCode get;set;

 [ForeignKey("PersonCountryCode")]
 public COUNTRIES Country get;set;


public class COUNTRIES
   
 [Key]
 public string CountryCode get;set:
 public string CountryName get;set:

在此示例中,当您删除人员时,国家/地区记录仍将保留在表中,但是如果您在删除人员记录时将 countrycode 的属性类型从 string 更改为 int,它也会从数据库中删除该国家/地区可能不是您想要的,为了防止这种情况,我们将属性定义为可为空的..

抱歉,如果我打错字了,我在添加代码示例时没有使用编译器。

编辑:

public class Animal
    
        [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public long Id  get; set; 
        public string Name  get; set; 
        
        public long ImageBinRef  get; set; 
        [ForeignKey("ImageBinRef")]
        public ImageBinary ImageBinary  get; set; 
    

【讨论】:

你错过了问题的本质。 Animal 课程在哪里?这是关于指向一个引用的两个类。需要与否都是无关紧要的。 @GertArnold 我已经相应地编辑了答案。我向他展示了如何做指点没关系。他也可以轻松做到这一点 是的,现在你有一些你显然没有测试过的东西。这真的不是你不编译就直接输入的东西,更不用说测试了。

以上是关于如何在 EF 中创建从两个表到一个源的一对一映射?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 python 中创建从绿色到红色的热图?

如何在 Android 11 中创建从图库中选择图像的意图?

我如何在表格中创建从一行到另一个仪表板的链接

如何在 Python 中创建从 Pub/Sub 到 GCS 的数据流管道

如何在 Flutter 中创建从左到右或从上到下的叠加飞溅动画

如何在Django中创建从Abstract User继承的多个用户配置文件?