Ale*_*lex 3 c# entity-framework-core ef-fluent-api
鉴于这些类:
public class A
{
public int Id {get; set;}
public int? BId {get; set;}
public B B {get; set;}
}
public class B
{
public int Id {get; set;}
public int? AId {get; set;}
public A A {get; set;}
}
Run Code Online (Sandbox Code Playgroud)
然后使用 Fluent API
modelBuilder.Entity<A>()
.HasOne(a => a.B)
.WithOne(b => b.A)
.HasForeignKey<A>(a => a.BId);
Run Code Online (Sandbox Code Playgroud)
创建对象并将它们添加到数据库时,相应表中的内容如下所示:
当我使用 EF Core 检索数据时:
我应该怎么做才能设置 B.AId?
这些0..1 : 0..1关系通常在实体之间定义,其中没有一个是明显的主体实体。我喜欢汽车和司机的例子,这比 A 和 B 更容易想象。
您所追求的模型如下所示:
有两个相互的外键,它们都有一个唯一的索引以在数据库级别强制执行 1:1。
在HasOne - WithOne这里不能使用组合,因为这总是需要一个HasForeignKey指令来告诉哪个实体是主体。这也仅将一个字段配置为外键。在您的示例中,B.AId只是一个常规字段。如果你不给它一个值,EF 也不会。
上述模型的映射比HasOne - WithOne:
var carEtb = modelBuilder.Entity<Car>();
var driverEtb = modelBuilder.Entity<Driver>();
carEtb.HasOne(c => c.Driver).WithMany();
carEtb.HasIndex(c => c.DriverID).IsUnique();
driverEtb.HasOne(d => d.Car).WithMany();
driverEtb.HasIndex(c => c.CarID).IsUnique();
Run Code Online (Sandbox Code Playgroud)
所以有两个 0..1:n 关联,它们通过外键上的索引变得唯一。
它创建了以下数据库模型:
var carEtb = modelBuilder.Entity<Car>();
var driverEtb = modelBuilder.Entity<Driver>();
carEtb.HasOne(c => c.Driver).WithMany();
carEtb.HasIndex(c => c.DriverID).IsUnique();
driverEtb.HasOne(d => d.Car).WithMany();
driverEtb.HasIndex(c => c.CarID).IsUnique();
Run Code Online (Sandbox Code Playgroud)
它创建两个可空的外键,它们都由一个唯一的过滤索引索引。完美的!
但...
EF 不认为这是双向的一对一关系。理所当然。两个 FK 就是这样,两个独立的 FK。然而,从数据完整性的角度来看,这种关系应该由两端建立:如果一个司机声称有一辆车(sets driver.CarID),那么这辆车也应该附加到这个司机(set car.DriverID)上,否则另一个司机可能会连接到它。
当现有的汽车和司机耦合时,可以使用一个小助手方法,例如Car:
CREATE TABLE [Drivers] (
[ID] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
[CarID] int NULL,
CONSTRAINT [PK_Drivers] PRIMARY KEY ([ID])
);
CREATE TABLE [Cars] (
[ID] int NOT NULL IDENTITY,
[Brand] nvarchar(max) NULL,
[Type] nvarchar(max) NULL,
[DriverID] int NULL,
CONSTRAINT [PK_Cars] PRIMARY KEY ([ID]),
CONSTRAINT [FK_Cars_Drivers_DriverID] FOREIGN KEY ([DriverID])
REFERENCES [Drivers] ([ID]) ON DELETE NO ACTION
);
CREATE UNIQUE INDEX [IX_Cars_DriverID] ON [Cars] ([DriverID])
WHERE [DriverID] IS NOT NULL;
CREATE UNIQUE INDEX [IX_Drivers_CarID] ON [Drivers] ([CarID])
WHERE [CarID] IS NOT NULL;
ALTER TABLE [Drivers] ADD CONSTRAINT [FK_Drivers_Cars_CarID] FOREIGN KEY ([CarID])
REFERENCES [Cars] ([ID]) ON DELETE NO ACTION;
Run Code Online (Sandbox Code Playgroud)
但是,当 aCar和Driver都在一个进程中创建和关联时,这是很笨拙的。EF 会抛出一个InvalidOperationException:
无法保存更改,因为在要保存的数据中检测到循环依赖关系:'Car [Added] <- Car { 'CarID' } Driver [Added] <- Driver { 'DriverID' } Car [Added]'。
意思是:其中一个FK可以一次性设置,而另一个只能在保存数据后设置。这需要SaveChanges在一段非常命令式的代码中包含在事务中的两个调用:
public void SetDriver(Driver driver)
{
Driver = driver;
driver.Car = this;
}
Run Code Online (Sandbox Code Playgroud)
所以现在我之所以用这些长度来解释所有这些:在我看来,0..1 : 0..1关联应该由具有唯一外键的联结表建模:
通过使用连接表 -
这个模型可以通过这个类模型来实现:
using (var db = new MyContext())
{
using (var t = db.Database.BeginTransaction())
{
var jag = new Car { Brand = "Jaguar", Type = "E" };
var peter = new Driver { Name = "Peter Sellers", Car = jag };
db.Drivers.Add(peter);
db.SaveChanges();
jag.Driver = peter;
db.SaveChanges();
t.Commit();
}
}
Run Code Online (Sandbox Code Playgroud)
和映射:
public class Car
{
public int ID { get; set; }
public string Brand { get; set; }
public string Type { get; set; }
public CarDriver CarDriver { get; set; }
}
public class Driver
{
public Driver()
{ }
public int ID { get; set; }
public string Name { get; set; }
public CarDriver CarDriver { get; set; }
}
public class CarDriver
{
public int CarID { get; set; }
public Car Car { get; set; }
public int DriverID { get; set; }
public virtual Driver Driver { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
现在创建车手和赛车和他们的协会可以很容易地在一个做SaveChanges呼叫:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var carDriverEtb = modelBuilder.Entity<CarDriver>();
carDriverEtb.HasKey(cd => new { cd.CarID, cd.DriverID });
carDriverEtb.HasIndex(cd => cd.CarID).IsUnique();
carDriverEtb.HasIndex(cd => cd.DriverID).IsUnique();
}
Run Code Online (Sandbox Code Playgroud)
唯一的缺点是,从navigtingCar到DriverVV是有点莱方便。好吧,您自己看看哪种型号最适合您。
| 归档时间: |
|
| 查看次数: |
2894 次 |
| 最近记录: |