实体框架Fluent API - 级联删除 - 带接口。

问题描述 投票:0回答:1

我在我的数据模型实体框架驱动(EFCore)数据库中定义了以下关系。

  1. A Person.cs 实现IRecord接口。A Person 拥有 ListExternalId.cs 对象。
  2. 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'.

我是否设置了错误的关系?我觉得这是由于我的关系属性是一个接口,我不得不投。

更新 - 2020-05-05

我注意到我生成的SQL表有三列,PersonId、RegulationId、RecordId。Person类的id存储在PersonId中,而不是RecordId。我可以看到这是在我前段时间的一次迁移中生成的。

我怀疑我可能在添加逆向属性和将Person类(以及Regulation类)标记为实现Record接口之前,已经添加了一次迁移并更新了数据库。

我怎样才能清理掉这些列,让我所有的IRecord对象都用RecordId引用?

enter image description here

c# sql-server entity-framework entity-framework-core
1个回答
-1
投票

该模型为我生成了一个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]);
© www.soinside.com 2019 - 2024. All rights reserved.