我在我的数据模型实体框架驱动(EFCore)数据库中定义了以下关系。
Person.cs
实现IRecord接口。A Person
拥有 List
的 ExternalId.cs
对象。ExternalId.cs
类有一个反向属性,回到 IRecord
接口。这是一个一对多的关系(一 IRecord
对许多 ExternalId
).所需的行为是,当 IRecord
对象被删除,依赖的ExternalIds也会被删除。
在我的数据库中,其他的modelstables也实现了这个接口(Regulation.cs
, Organization.cs
等)和它们所引用的ExternalIds受制于相同的 OnDelete
行为。
这是我在 ApplicationDbContext.cs
:
modelBuilder.Entity<Customer>()
.HasMany(rec => rec.ExternalIds)
.WithOne(extId => (Customer)extId.Record)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Person>()
.HasMany(rec => rec.ExternalIds)
.WithOne(extId => (Person)extId.Record)
.OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<Organization>()
.HasMany(rec => rec.ExternalIds)
.WithOne(extId => (Organization)extId.Record)
.OnDelete(DeleteBehavior.Cascade);
我到底做错了什么,以至于没有达到预期效果?
以上是所涉及的不同模式。
IRecord:
public interface IRecord {
int Id { get; set; }
}
ExternalId:
public class ExternalId : IRecord
{
public int Id { get; set; }
[InverseProperty("ExternalIds")]
public IRecord Record { get; set; }
public string Service { get; set; } // Comes from DataSource Enum
public string Purpose { get; set; } // Comes from IdType Enum
public string Value { get; set; } // Value
}
Person
public class Person: IRecord
{
#region Properties
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType(DataType.Date)]
public DateTime? DateOfBirth { get; set; }
public int ContactInformationId { get; set; }
public virtual ContactInformation ContactInformation { get; set; }
public string PictureURL { get; set; }
public string Title { get; set; }
public List<ExternalId> ExternalIds { get; set; }
}
这些是表名。
public DbSet<ExternalId> Identifiers { get; set; }
public DbSet<Person> People { get; set; }
当我试图删除数据库中的Person对象时(通过) _context.People.RemoveRange(peopleToDelete)
)我得到以下错误。
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details.
---> Microsoft.Data.SqlClient.SqlException (0x80131904): The DELETE statement conflicted with the REFERENCE constraint "FK_Identifiers_People_PersonId". The conflict occurred in database "OurGov", table "dbo.Identifiers", column 'PersonId'.
The DELETE statement conflicted with the REFERENCE constraint "FK_Identifiers_People_PersonId". The conflict occurred in database "OurGov", table "dbo.Identifiers", column 'PersonId'.
我是否设置了错误的关系?我觉得这是由于我的关系属性是一个接口,我不得不投。
我注意到我生成的SQL表有三列,PersonId、RegulationId、RecordId。Person类的id存储在PersonId中,而不是RecordId。我可以看到这是在我前段时间的一次迁移中生成的。
我怀疑我可能在添加逆向属性和将Person类(以及Regulation类)标记为实现Record接口之前,已经添加了一次迁移并更新了数据库。
我怎样才能清理掉这些列,让我所有的IRecord对象都用RecordId引用?
该模型为我生成了一个ON DELETE CASCADE外键,例如
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.SqlServer;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
namespace EfCore3Test
{
public interface IRecord
{
int Id { get; set; }
}
public class Person : IRecord
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[DataType(DataType.Date)]
public DateTime? DateOfBirth { get; set; }
public int ContactInformationId { get; set; }
// public virtual ContactInformation ContactInformation { get; set; }
public string PictureURL { get; set; }
public string Title { get; set; }
public List<ExternalId> ExternalIds { get; set; }
}
public class ExternalId : IRecord
{
public int Id { get; set; }
[InverseProperty("ExternalIds")]
public IRecord Record { get; set; }
public string Service { get; set; } // Comes from DataSource Enum
public string Purpose { get; set; } // Comes from IdType Enum
public string Value { get; set; } // Value
}
public class Db : DbContext
{
public DbSet<ExternalId> Identifiers { get; set; }
public DbSet<Person> People { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Person>()
.HasMany(rec => rec.ExternalIds)
.WithOne(extId => (Person)extId.Record)
.OnDelete(DeleteBehavior.Cascade);
}
private static readonly ILoggerFactory loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) =>
category == DbLoggerCategory.Database.Command.Name
&& level == LogLevel.Information).AddConsole();
});
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseLoggerFactory(loggerFactory)
.UseSqlServer("Server=.;database=EfCore3Test;Integrated Security=true",
o => o.UseRelationalNulls());
base.OnConfiguring(optionsBuilder);
}
}
class Program
{
static void Main(string[] args)
{
using var db = new Db();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
}
}
}
产出
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (15ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
IF SERVERPROPERTY('EngineEdition') <> 5
BEGIN
ALTER DATABASE [EfCore3Test] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
END;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (28ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
DROP DATABASE [EfCore3Test];
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (121ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
CREATE DATABASE [EfCore3Test];
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (39ms) [Parameters=[], CommandType='Text', CommandTimeout='60']
IF SERVERPROPERTY('EngineEdition') <> 5
BEGIN
ALTER DATABASE [EfCore3Test] SET READ_COMMITTED_SNAPSHOT ON;
END;
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT 1
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [People] (
[Id] int NOT NULL IDENTITY,
[FirstName] nvarchar(max) NULL,
[LastName] nvarchar(max) NULL,
[DateOfBirth] datetime2 NULL,
[ContactInformationId] int NOT NULL,
[PictureURL] nvarchar(max) NULL,
[Title] nvarchar(max) NULL,
CONSTRAINT [PK_People] PRIMARY KEY ([Id])
);
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE [Identifiers] (
[Id] int NOT NULL IDENTITY,
[RecordId] int NULL,
[Service] nvarchar(max) NULL,
[Purpose] nvarchar(max) NULL,
[Value] nvarchar(max) NULL,
CONSTRAINT [PK_Identifiers] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Identifiers_People_RecordId] FOREIGN KEY ([RecordId]) REFERENCES [People] ([Id]) ON DELETE CASCADE
);
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE INDEX [IX_Identifiers_RecordId] ON [Identifiers] ([RecordId]);