EF Code 首先,如何在 EF 中映射双向一对一或零关系?

EF Code first, how can you map a bidirectional one to one or zero relationship in EF?

我有两个 类 一个用户和一个图像 table:

public class User
{
    public Guid? ImageId {get; set;}
    public Image Image {get; set;}
}

public class Image
{
    public Guid? UserId {get; set;}
    public User User {get; set;}
}

用户和图像都可以在没有其他实体的情况下独立存在,但是如果它们确实有关系,则用户只能与一个图像相关联,并且图像只能有用户,如果它们是不为空。我如何映射这个?目前我有:

public UserMapping()
    {            
        HasOptional(x => x.ProfileImage).WithOptionalPrincipal(x => 
        x.User).Map(x => x.MapKey("UserId"));
    }

并且 ImageMapping 上没有任何内容,因为从其他答案中可以看出,不要将关系映射两次,否则它会吓坏。但是,迁移文件最终会在图像 table 上生成额外的 User_Id 属性 然后:

CreateTable(
            "dbo.Images",
            c => new
                {
                    Id = c.Guid(nullable: false),
                    UserId = c.Guid(),
                    User_Id = c.Guid(),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.Users", t => t.User_Id)
            .Index(t => t.User_Id);

这是错误的。我怎样才能正确地进行映射?

编辑:我也找到了这个 并尝试了原始问题中显示的声称有效的内容,但它没有,仍然创建 User_Id .

查看 this page 了解更多详情。

基本上,一对一关系是主体实体的 PK 作为从属实体中的 PK 和 FK 传递的关系。据我所知,您需要将两个实体映射为彼此之间的可选实体,而这在 EF 中是不可能的,至少不是一对零或一。

我知道您希望双方都是可选的,但事实证明 EF 需要知道您的实体中的哪一个是委托人。所以这是一种改变关系的方法。我建议你在关系中定义你的主要实体,另一个成为可选的。例如:

作为主体的用户:

//Your entities
public class Image
{
    public Guid UserId { get; set; }

    public virtual User User { get; set; }
}

public class User
{
    public Guid UserId { get; set; }
    
    public virtual Image Image { get; set; }
}

//Mappings:
public class ImageMappingConfiguration : EntityTypeConfiguration<Image>
    {
        public ImageMappingConfiguration()
        {
            HasKey(x => x.UserId);
        }
    }

 public class UserMappingConfiguration : EntityTypeConfiguration<User>
    {
        public UserMappingConfiguration()
        {
            HasKey(x => x.UserId);

            HasOptional(x => x.Image)
                .WithRequired(x => x.User);
        }
    }

添加迁移后你会得到这个:

public override void Up()
{
    CreateTable(
            "dbo.Images",
            c => new
            {
                UserId = c.Guid(nullable: false)
            })
        .PrimaryKey(t => t.UserId)
        .ForeignKey("dbo.Users", t => t.UserId)
        .Index(t => t.UserId);

    CreateTable(
            "dbo.Users",
            c => new
            {
                UserId = c.Guid(nullable: false)
            })
        .PrimaryKey(t => t.UserId);
}

看到作为 PK 和 FK 传递给 Image 的 User 的主键了吗?这就是 EF 处理一对零或一关系的方式。

UPDATE! Two One-To-Many relationships with Unique Constraints.

让我们试试这个方法。让我知道这是否适合你。

public sealed class Image
{
    public Image()
    {
        Users = new List<User>();
    }

    public Guid Id { get; set; }

    public Guid? UserId { get; set; }

    public List<User> Users { get; set; }
        
    public User User { get; set; }
}

public sealed class User
{
    public User()
    {
        Images = new List<Image>();
    }
            
    public Guid Id { get; set; }

    public Guid? ImageId { get; set; }

    public List<Image> Images { get; set; }

    public Image Image { get; set; }
}

//Mappings:
public class ImageMappingConfiguration : EntityTypeConfiguration<Image>
{
    public ImageMappingConfiguration()
    {
        HasKey(x => x.Id);

        Property(x => x.UserId)
            .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_ImageMustBeUnique")
            {
                IsUnique = true
            }));


        HasMany(x => x.Users)
            .WithOptional(x => x.Image)
            .HasForeignKey(x => x.ImageId)
            .WillCascadeOnDelete(true);
    }
}

public class UserMappingConfiguration : EntityTypeConfiguration<User>
{
    public UserMappingConfiguration()
    {
        HasKey(x => x.Id);

        Property(x => x.ImageId)
            .HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute("IX_UserMustBeUnique")
            {
                IsUnique = true
            }));

        HasMany(x => x.Images)
            .WithOptional(x => x.User)
            .HasForeignKey(x => x.UserId);
    }
}

用法:

//test adding a user and an image.
var user = new User
{
    Id = Guid.NewGuid(),
};
var image = new Image
{
    Id = Guid.NewGuid(),
};

using (var ctx = new Context())
{
    ctx.Users.Add(user);
    ctx.Images.Add(image);
    ctx.SaveChanges();

    //associate them
    user.Images.Add(image);
    image.Users.Add(user);
    ctx.SaveChanges();

    //try to add a second image to the user
    var image2 = new Image
    {
        Id = Guid.NewGuid()
    };

    try
    {
        user.Images.Add(image2);
        ctx.SaveChanges();
    }
    catch (DbUpdateException ex)
    {
        Console.WriteLine(ex);
    }
}