C# EF代码第一个一对一和一对多映射

C# EF代码第一个一对一和一对多映射,c#,entity-framework,ef-code-first,ef-model-builder,C#,Entity Framework,Ef Code First,Ef Model Builder,我有下面的模型,我试图从相同的父实体和子实体构建一对一和一对多的关系。一对多关系适用于我当前的映射,但我正在努力添加新的一对一关系(用于CoverPicture属性)。以下是相关模型和EF映射代码: C类: public int Id { get; set; } public string Name { get; set; } public Guid? CoverPictureId { get; set; } public virtual Picture CoverPicture { get;

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

C类:

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; }
相关类别
EntityTypeConfiguration
映射(不正确):

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

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

以下是EF构建的
Up()
迁移代码:

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

我做错了什么?

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

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

在一对一关系中,一端必须是主体,另一端必须是依赖的。在配置这种关系时,实体框架要求依赖关系的主键也是外键。在您的配置中,主体是
类别
,从属是
图片
。这就是FK
CoverPictureId
int
而不是
Guid
,因为这确实是迁移代码所解释的
类别
PK。

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

类型“YourProject.Category”上声明的导航属性“Pictures”已配置为具有冲突的多重性

那为什么呢?让我们看看您的映射,以及您用通俗易懂的英语告诉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);
但是让我们停下来想一想。您不仅基本上在两个方向上定义了1对多关系(不要与多对多混淆),而且还破坏了模型的完整性。现在,您可以将
图片
指定为
类别
封面图片,即使该
图片
不属于该
类别
。这不是你想要的,最终会让你头疼。与其在
类别
上明确定义
封面图片
属性,不如这样做

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; }
}
公共类类别
{
公共int Id{get;set;}
公共字符串名称{get;set;}
公共虚拟ICollection图片{get;set;}
公共设置封面图片(图片)
{
如果(!Pictures.Contains(picture))
{
抛出新的ArgumentException(“图片不在此类别中”);
}
var currentCoverPicture=Pictures.FirstOrDefault(p=p.IsCoverPicture==true);
如果(CurrentCoverPicture e!=null)
{
currentCoverPicture.IsCoverPicture=false;
}
picture.IsCoverPicture=真;
}
}
公开课图片
{
公共Guid Id{get;set;}
公共字符串文件名{get;set;}
公共int类_Id{get;set;}
公共虚拟类别{get;set;}
公共bool IsCoverPicture{get;受保护的内部集;}
}
这将强制执行您的不变量(业务规则),该不变量表示

  • 类别的
    封面图片
    必须属于该
    类别
    (由数据库强制执行)和
  • 只能有1张封面图片 对于
    类别
    (在代码中强制执行)

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

    您使用的是什么版本的EF?
    this.HasOptional(p => p.CoverPicture) //A Category may or may not have 1 Picture 
    .WithMany()                           //A Picture can have many Categories
    
    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);
    
    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; }
    }