2

我有以下模型,我正在尝试从相同的父实体和子实体构建一对一以及一对多的关系。一对多关系适用于我当前的映射,但我正在努力添加新的一对一关系(对于 CoverPicture 属性)。以下是相关模型和EF映射代码:

类别.cs:

public int Id { get; set; }
public string Name { get; set; }
public Guid? CoverPictureId { get; set; }
public virtual Picture CoverPicture { get; set; }
public virtual ICollection<Picture> Pictures { get; set; }

图片.cs:

public Guid Id { get; set; }
public string FileName { get; set; }
public int CategoryId { get; set; }
public virtual Category Category { get; set; }

相关类别EntityTypeConfiguration<Category>映射(不正确):

this.HasOptional(t => t.CoverPicture)
    .WithRequired()
    .Map(m => m.MapKey("CoverPictureId"))
    .WillCascadeOnDelete(false);

相关图片EntityTypeConfiguration<Picture>映射(正确):

this.HasRequired(t => t.Category)
    .WithMany(t => t.Pictures)
    .HasForeignKey(k => k.CategoryId);

当我尝试为新CoverPicture属性添加迁移时,EF 尝试向表中添加一CoverPictureIdCategory(这是我想要的),但也CoverPictureIdPicture表中添加一列(这不是我想要的;Picture已经定义和映射了一个键)。

下面是Up()EF 搭建的迁移代码:

AddColumn("dbo.Category", "CoverPictureId", c => c.Guid());
AddColumn("dbo.Picture", "CoverPictureId", c => c.Int());
CreateIndex("dbo.Picture", "CoverPictureId");
AddForeignKey("dbo.Picture", "CoverPictureId", "dbo.Category", "Id");

我究竟做错了什么?

4

2 回答 2

4

我不确定您使用的是哪个版本的 EF,但较新的版本不允许您执行您尝试执行的映射类型,您将收到以下错误:

在类型“YourProject.Category”上声明的导航属性“图片”已配置有冲突的多重性。

那为什么呢?让我们看看您的映射,以及您用简单的英语告诉 Entity Framework 的内容:

this.HasRequired(t => t.Category)  //A Picture must have 1 Category
.WithMany(t => t.Pictures)         //A Category can have many Pictures


this.HasOptional(t => t.CoverPicture) //A Category may or may not have 1 Picture
.WithRequired()                       //A Picture must have 1 Category

将其与 octavioccl 的答案进行比较:

this.HasOptional(p => p.CoverPicture) //A Category may or may not have 1 Picture 
.WithMany()                           //A Picture can have many Categories

从 WithRequired 到 WithMany 的变化是交换外键的放置位置。现在你有了你正在寻找的映射......有点:

CreateTable(
    "dbo.Categories",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            Name = c.String(),
            CoverPicture_Id = c.Guid(),
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.Pictures", t => t.CoverPicture_Id)
    .Index(t => t.CoverPicture_Id);

CreateTable(
    "dbo.Pictures",
    c => new
        {
            Id = c.Guid(nullable: false),
            FileName = c.String(),
            Category_Id = c.Int(nullable: false),
        })
    .PrimaryKey(t => t.Id)
    .ForeignKey("dbo.Categories", t => t.Category_Id, cascadeDelete: true)
    .Index(t => t.Category_Id);

但是让我们停下来想一想。您不仅基本上定义了双向的一对多关系(不要与多对多混淆),而且还破坏了模型的完整性。现在您可以将 a 指定Picture为 a CategoryCoverPicture即使它Picture不属于 that Category。这不是你想要的,最终会让你头疼。与其明确定义CoverPictureon 属性Category,不如这样?

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Picture> Pictures { get; set; }

    public SetCoverPicture(Picture picture)
    {
        if(!Pictures.Contains(picture))
        {
            throw new ArgumentException("Picture is not in this Category");
        }
        var currentCoverPicture = Pictures.FirstOrDefault(p=p.IsCoverPicture == true);
        if(currentCoverPictur e!= null)
        {
            currentCoverPicture.IsCoverPicture = false;
        }
        picture.IsCoverPicture = true;
    }

}

public class Picture
{
    public Guid Id { get; set; }
    public string FileName { get; set; }
    public int Category_Id { get; set; }
    public virtual Category Category { get; set; }
    public bool IsCoverPicture { get; protected internal set; }
}

这会强制执行您的不变量(业务规则),说明...

  1. a CoverPicturefor a Categorymust 属于那个Category (由数据库强制执行)和
  2. a只能有 1CoverPictureCategory(在代码中强制执行)

您可以使用 octavioccl 提供的代码执行类似的操作,但此处提供的代码会产生更清晰、更易于理解的物理数据模型。

于 2015-01-29T17:24:55.027 回答
0

要解决您的问题,请尝试映射该关系,如下所示:

this.HasOptional(p => p.CoverPicture)
            .WithMany().HasForeignKey(p=>p.CoverPictureId)
            .WillCascadeOnDelete(false);

在一对一的关系中,一端必须是主体,而另一端必须是从属的。在配置这种关系时,Entity Framework 要求依赖的主键也是外键。使用您的配置,主体是Category,依赖项是Picture. CoverPictureId这就是FK 的方式intGuid因为这确实是Category您的迁移代码所解释的 PK。

于 2015-01-29T16:33:37.540 回答