简体   繁体   English

Entity Framework Fluent API - 级联删除 - 带接口

[英]Entity Framework Fluent API - Cascade Delete - With Interface

I've got the following relationships defined in my data model / Entity Framework-driven (EFCore) database:我的数据 model / Entity Framework-driven (EFCore) 数据库中定义了以下关系:

  1. A Person.cs which implements the IRecord interface.一个实现 IRecord 接口的Person.cs A Person has a List of ExternalId.cs objects.一个Person有一个ExternalId.cs对象的List
  2. The ExternalId.cs class has an inverse property back to the IRecord interface. ExternalId.cs class 具有返回到IRecord接口的反向属性。 This is a one-to-many relationship (one IRecord to many ExternalId ).这是一对多的关系(一个IRecord到多个ExternalId )。

The desired behavior is that when the IRecord object is deleted, the dependent ExternalIds are too.期望的行为是,当删除IRecord object 时,依赖的 ExternalIds 也是。

Other models/tables in my database also implement this interface ( Regulation.cs , Organization.cs , etc.) and the ExternalIds they reference subject to the same OnDelete behavior.我的数据库中的其他模型/表也实现了这个接口( Regulation.csOrganization.cs等),它们引用的 ExternalIds 也遵循相同的OnDelete行为。

This is what I set up in ApplicationDbContext.cs :这是我在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);

What am I doing wrong that this is not working as expected?我做错了什么,这没有按预期工作?

These are the different models involved above:这些是上面涉及的不同模型:

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; }
}

These are the table names:这些是表名:

public DbSet<ExternalId> Identifiers { get; set; }
public DbSet<Person> People { get; set; }

When I attempt to delete the Person objects in my database (via _context.People.RemoveRange(peopleToDelete) ) I get the following error:当我尝试删除数据库中的 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'.

Did I set the relationships up incorrectly?我是否错误地设置了关系? I feel like this is due to my relationship property being an interface that I had to cast.我觉得这是因为我的关系属性是我必须强制转换的接口。

Update - 2020-05-05更新 - 2020-05-05

I notice that my SQL Table that was generated has three columns, PersonId, RegulationId, RecordId.我注意到我生成的 SQL 表有三列,PersonId、RegulationId、RecordId。 The Person class has its id stored in PersonId as opposed to RecordId. Person class 的 id 存储在 PersonId 中,而不是 RecordId 中。 I can see this was generated in one of my migrations awhile back.我可以看到这是在不久前的一次迁移中生成的。

I suspect I may have Added a migration and Updated the database prior to adding the inverse property and/or marking Person class (as well as the Regulation class) as implementing the Record interface.我怀疑我可能在添加逆属性和/或将 Person class(以及规则类)标记为实现 Record 接口之前添加了迁移并更新了数据库。

How can I clean this up to drop those columns and have all of my IRecord objects referenced in RecordId?如何清理它以删除这些列并在 RecordId 中引用我的所有 IRecord 对象?

在此处输入图像描述

That model generated an ON DELETE CASCADE Foreign Key for me.那个 model 为我生成了一个 ON DELETE CASCADE 外键。 eg例如

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();
        }
    }
}

outputs输出

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]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM