【问题标题】:Implementing One to Zero-Or-One relationship in EF Code first by data annotation首先通过数据注释在EF Code中实现一对零或一的关系
【发布时间】:2017-01-21 16:08:48
【问题描述】:

db 中有两个实体,Cars 和 Car's image:

public class Car
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

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

public class CarImage
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

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

    [ForeignKey("CarID")]
    public virtual Car Car { get; set; }

    public string FileName { get; set; }

    public bool IsPrimaryImage { get; set; }
}

一切都很好,工作正常。示例:car with car is = 123 有 5 张图片,其中一张已标记为“主要”(第一个上传的图片或版主手动选择)。

然后我决定优化 db 模型:删除 IsPrimaryImage 并将 PrimaryImage 添加到 Car 中,如下所示:

public class CarImage
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

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

    [ForeignKey("CarID")]
    public virtual Car Car { get; set; }

    public string FileName { get; set; }
}

public class Car
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

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

    public int? PrimaryImageID { get; set; }

    public virtual CarImage PrimaryImage { get; set; }
}

(汽车可能不包含图像 - 这就是为什么int?,而不是int

编译,运行 -- 失败并出现错误:

“System.InvalidOperationException”类型的异常发生在 EntityFramework.dll 但未在用户代码中处理

附加信息:无法确定主端 类型“MyApp.Domain.CarImage”和 'MyApp.Domain.Car'。该关联的主体端必须是 使用关系流式 API 或数据显式配置 注释。

在stackoverlow上发现了很多关于这个错误的类似主题:

  • first,公地
  • second,同样的错误,但 1:1 的关系,不是我的情况
  • third,看起来像我的情况,因为大约 1:0..1 但只有流畅的 API。

是否可以使用数据注释修复错误? EF 版本为 6。

【问题讨论】:

  • 你不使用 Fluent API 吗?
  • @SoheilAlizadeh 当然,我更喜欢数据注释 - 它看起来(对我来说)更清晰。

标签: c# entity-framework


【解决方案1】:

找到正确的方法:

public class Car
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int CarID { get; set; }

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

    public int? PrimaryImageID { get; set; }

    [ForeignKey("CarImageID")]
    public virtual CarImage PrimaryImage { get; set; }
}

public class CarImage
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int CarImageID { get; set; }

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

    [ForeignKey("CarID")]
    [Required]
    public virtual Car Car { get; set; }

    public string FileName { get; set; }
}

附: 很遗憾,这不适用于此类 ID:

public class Car
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }
}

public class CarImage
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }
}

更新:如果您将属性指定为 ID 并将 ForeignKey 指定为 Id - 这将导致错误:

“System.InvalidOperationException”类型的异常发生在 EntityFramework.dll 但未在用户代码中处理

附加信息:ForeignKeyAttribute 属性 “MyApp.Domain.Car”类型上的“PrimaryImage”无效。外国的 在依赖类型上找不到键名“Id” 'MyApp.Domain.CarImage'。 Name 值应以逗号分隔 外键属性名称列表。

所以,这也有效(区分大小写!):

public class Car
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

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

    public int? PrimaryImageID { get; set; }

    [ForeignKey("ID")]
    public virtual CarImage PrimaryImage { get; set; }
}

和:

public class CarImage
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

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

    [ForeignKey("ID")]
    [Required]
    public virtual Car Car { get; set; }

    public string FileName { get; set; }
}

【讨论】:

    【解决方案2】:

    在这种特殊情况下,您不需要数据注释。您需要帮助 EF 找出一个 Car '有'多个 Images,而不是 Car 和 Image 是相互指向的随机实体。

    public class Car
    {
      ...
    
      public virtual ICollection<CarImage> Images { get; set; } // <-- Adding this relation makes the problem go away :-)
    
      [ForeignKey("PrimaryImageID")]
      public virtual CarImage PrimaryImage { get; set; }
      public int? PrimaryImageID { get; set; }
    }
    

    由 A K 更新:完整代码是(在 virtual Car Car 上没有 Required 的情况下工作,并且它适用于我的 ID 名称样式指南)

    public class Car
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }
    
        [Required]
        public string Title { get; set; }
    
        public int? PrimaryImageID { get; set; }
    
        [ForeignKey("PrimaryImageID")]
        public virtual CarImage PrimaryImage { get; set; }
    
        public virtual ICollection<CarImage> Images { get; set; }
    }
    

    public class CarImage
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ID { get; set; }
    
        [Required]
        public int CarID { get; set; }
    
        [ForeignKey("CarID")]
        public virtual Car Car { get; set; }
    
        public string FileName { get; set; }
    }
    

    【讨论】:

    • 就像我自己的答案一样,这也有效。而且,这看起来比我的版本更漂亮:它适用于两个主键(CarID -> ID)。我可以在接受之前编辑您的答案吗(添加 sql management studio 的截图和完整的代码示例)?
    • 拜托了!严格来说,我实现的模型与您最初提出的模型略有不同,您自己的答案是一种更直接的方法。我倾向于设置模型以避免 EF(以及大多数其他 ORM)中真正的 oneone 关系,因为它们通常不会被很好地处理。
    • 再次感谢您的帮助。我找到了使用 prop ID 修复错误的方法,请参阅我的答案中的更新。我会将我的解决方案标记为可供未来访问者接受,您的版本不太可接受。
    猜你喜欢
    • 2013-01-20
    • 1970-01-01
    • 1970-01-01
    • 2019-07-25
    • 2021-06-25
    • 1970-01-01
    • 2011-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多