EF 代码首先可选到单个 FK 列可选

EF Code first optional to optional with a single FK column

我正在尝试建立可选关系。我想我已经很接近了,但出于某种原因,Entity Framework 无法在我的实体上使用预定义的 FK 属性。

public class Foo
{
    public Guid? BarId { get; set; }
    public virtual Bar Bar { get; set; }
}

public class Bar
{
    public virtual Foo Foo { get; set; }
}

这正是我想在数据库中看到的。 Foo 有 FK 列,但 Bar 没有到 Foo 的 FK。

流利的API:

modelBuilder
    .Entity<Foo>()
    .HasOptional(f => f.Bar)
    .WithOptionalDependent(b => b.Foo);

但是,当我生成数据库时,EF 已将 FooId 属性 设置为简单标量 属性,并创建了它自己的 Foo_Id 它使用为了 FK。

其他一切如我所料,Bar table 没有 FK 字段到 Foo

在网上查找,我找到了执行以下操作的建议:

modelBuilder
    .Entity<Foo>()
    .HasOptional(f => f.Bar)
    .WithOptionalDependent(b => b.Foo)
    .Map(d => d.MapKey("BarId"));

但是,这不起作用,除非我从我的 Foo class 中删除 BarId,我不想那样做。我特别想保留 BarId 以便在需要时可以用它来 create/destroy 关系。

如何让 EF 使用我创建的 FK 属性,而不是生成一个额外的列并使用它?

旁注:我不想使用数据注释。整个解决方案围绕将实体(域项目)与数据提供者(数据库 EF 项目)分离而构建。我们专门使用 Fluent API 进行设置。


为了完整起见,目的是 Bar 可以有多个这样的关系:

public class ADifferentFoo
{
    public Guid? BarId { get; set; }
    public virtual Bar Bar { get; set; }
}

这就是我不想在 Bar 中有 FK 的原因:你最终会为每个 FK 创建一列,而且我们知道一个 Bar 只会有一个关系。


也许一个具体的例子会有所帮助:

Foo = Person
Bar = Picture
ADifferentFoo = Car

一个人可以选择拥有一张照片。一辆汽车可以选择有一张图片。

一张图片总是属于汽车异或一个人,永远不会属于两者。
但据我所知,这不需要在数据库级别明确强制执行。业务逻辑可以处理。

同样,我不关心没有任何 Car 或 Person 的 Pictures - 我不需要它们,但如果数据模型允许它们存在,这不是问题。业务逻辑也将弥补这一差距。

怎么样:

public class Person
{
    public int PersonID { get; set; }
    public int PictureId { get; set; }
    public virtual Picture Picture { get; set; }

}
public class Car
{
    public int CarId { get; set; }
    public int PictureId { get; set; }
    public virtual Picture Picture { get; set; }
}

public class Picture
{
    public int PictureId { get; set; }
}

?

how do you propose to enforce it with FKs in the Picture table?

您可以使用 CHECK CONSTRAINT

以上都是假设 EF 6,并且是一对多关系。

对于 1-1,FK 必须是一个键,而 EF 6 不支持多个键。

EF Core 具有第一个class 备用键概念,您不仅可以使用指向备用键的 FK,还可以使用 一个替代键来实现一对一的关系。 EG

using Microsoft.EntityFrameworkCore;
using System.Linq;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;

namespace efCoreTest
{

    public class Person
    {
        public int PersonID { get; set; }
        public int? PictureId { get; set; }
        public virtual Picture Picture { get; set; }

    }
    public class Car
    {
        public int CarId { get; set; }
        public int? PictureId { get; set; }
        public virtual Picture Picture { get; set; }
    }

    public class Picture
    {
        public int PictureId { get; set; }
        public virtual Car Car { get; set; }

        public virtual Person Person { get; set; }
    }


    public partial class Db : DbContext
    {
        public DbSet<Car> Cars { get; set; }
        public DbSet<Picture> Pictures { get; set; }

        public DbSet<Person> Persons { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Car>().HasAlternateKey(c => c.PictureId);
            modelBuilder.Entity<Person>().HasAlternateKey(c => c.PictureId);

            modelBuilder.Entity<Car>().HasOne(c => c.Picture).WithOne(p => p.Car);
            modelBuilder.Entity<Person>().HasOne(c => c.Picture).WithOne(p => p.Person);
            base.OnModelCreating(modelBuilder);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer("Server=localhost;database=efcore2test;integrated security=true");
            base.OnConfiguring(optionsBuilder);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new Db())
            {


                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                var c = new Car();
                var p = new Person();

                var pic = new Picture();
                pic.Car = c;
                pic.Person = p;

                db.Add(c);
                db.Add(p);
                db.Add(pic);

                db.SaveChanges();


            }

            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();
        }
    }
}

请注意,此模型确实允许汽车和人共享一张照片。