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();
}
}
}
请注意,此模型确实允许汽车和人共享一张照片。
我正在尝试建立可选关系。我想我已经很接近了,但出于某种原因,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();
}
}
}
请注意,此模型确实允许汽车和人共享一张照片。