为在其他实体中引用的实体实施硬删除

Implementation of Hard Delete for Entity which is being referred in other entity

我正在尝试为 FullyAuditedEntity 实体 Test 实施硬删除。 Test 的主键是 Id,它被称为 TestTest2 实体作为外键。当我尝试从 Test 实体中删除一条记录时,出现以下错误。

我已按照 问题进行实施。

TestAppService

public async Task DeleteTest(EntityDto<string> input)
{
    using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.SoftDelete))
    {
        await _TestRepository.DeleteTest(input);
        CurrentUnitOfWork.SaveChanges();
    }
}

测试库

public async Task DeleteArticle(EntityDto input)
{
    await DeleteAsync(x => x.Id == input.Id);
}

测试测试2

[Table("TestTest2")]
public class TestTest2 : FullAuditedEntity
{
    [ForeignKey("TestId")]
    public virtual Test Test { get; set; }
    public virtual string TestId { get; set; }

    [ForeignKey("Test2Id")]
    public virtual Test2Details Test2s { get; set; }
    public virtual int Test2Id { get; set; }
}

MyProjectDbContextModelSnapshot

modelBuilder.Entity("MyCompany.MyProject.Business.Model.Tests.TestTest2Association", b =>
{
    b.HasOne("MyCompany.MyProject.Business.Model.Tests.Test", "Test")
        .WithMany()
        .HasForeignKey("TestId");

    b.HasOne("MyCompany.MyProject.Business.Model.Test2s.Test2Details", "Test2s")
        .WithMany()
        .HasForeignKey("Test2Id")
        .OnDelete(DeleteBehavior.Cascade);
});

ERROR 2018-02-28 18:10:09,840 [26 ] Mvc.ExceptionHandling.AbpExceptionFilter - An error occurred while updating the entries. See the inner exception for details. Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> System.Data.SqlClient.SqlException: The DELETE statement conflicted with the REFERENCE constraint "FK_TestTest2_Test_TestId". The conflict occurred in database "MyProjectDb", table "dbo.TestTest2", column 'TestId'. The statement has been terminated. at System.Data.SqlClient.SqlConMyCompanytion.OnError(SqlException exception, Boolean breakConMyCompanytion, Action1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConMyCompanytion.OnError(SqlException exception, Boolean breakConMyCompanytion, Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConMyCompanytionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean asyncWrite, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior) at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) at System.Data.Common.DbCommand.ExecuteReader() at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConMyCompanytion conMyCompanytion, DbCommandMethod executeMethod, IReadOnlyDictionary2 parameterValues) at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteReader(IRelationalConMyCompanytion conMyCompanytion, IReadOnlyDictionary2 parameterValues) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConMyCompanytion conMyCompanytion) --- End of inner exception stack trace --- at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConMyCompanytion conMyCompanytion) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(Tuple2 parameters) at Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func3 operation, Func3 verifySucceeded) at Microsoft.EntityFrameworkCore.ExecutionStrategyExtensions.Execute[TState,TResult](IExecutionStrategy strategy, TState state, Func2 operation) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConMyCompanytion conMyCompanytion) at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IReadOnlyList1 entries) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList1 entriesToSave) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess) at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess) at Abp.EntityFrameworkCore.AbpDbContext.SaveChanges() in D:\Github\aspnetboilerplate\src\Abp.EntityFrameworkCore\EntityFrameworkCore\AbpDbContext.cs:line 198 at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext`3.SaveChanges() in D:\Github\aspnetboilerplate\src\Abp.ZeroCore.EntityFrameworkCore\Zero\EntityFrameworkCore\AbpZeroCommonDbContext.cs:line 154 at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesInDbContext(DbContext dbContext) in D:\Github\aspnetboilerplate\src\Abp.EntityFrameworkCore\EntityFrameworkCore\Uow\EfCoreUnitOfWork.cs:line 159 at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChanges() in D:\Github\aspnetboilerplate\src\Abp.EntityFrameworkCore\EntityFrameworkCore\Uow\EfCoreUnitOfWork.cs:line 60 at MyCompany.MyProject.Business.Services.Tests.TestAppService.d__12.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() INFO 2018-02-28 18:10:09,873 [26 ] etCore.Mvc.Internal.ObjectResultExecutor - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. INFO 2018-02-28 18:10:09,922 [26 ] ore.Mvc.Internal.ControllerActionInvoker - Executed action MyCompany.MyProject.Business.Services.Tests.TestAppService.DeleteTest (MyCompany.MyProject.Business.Services) in 3940.4091ms INFO 2018-02-28 18:10:10,158 [26 ] soft.AspNetCore.Hosting.Internal.WebHost - Request finished in 4037.4507ms 500 application/json; charset=utf-8

注意:应该删除TestTest2tables.

中的条目

更新

下面的答案效果很好,但我有一些具体要求。 Test 实体 Id 在其他 table 中被引用,例如 TestTest2TestTest3TestTest4TestTest5。当我从 Test table 中删除一条记录时,它应该从所有 table 中删除。 但我还需要调用其他依赖的tables(例如TestTest2TestTest3TestTest4TestTest5)删除方法来做一些特定于该实体的额外清理(例如 TestTest2TestTest3TestTest4TestTest5)。

测试库

public async Task DeleteTest(EntityDto input)
{
    await DeleteAsync(input.Id);

    _TestTest2Repository.Delete(x => x.TestId == input.Id);
    _TestTest3Repository.Delete(x => x.TestId == input.Id);
    _TestTest4Repository.Delete(x => x.TestId == input.Id);
    _TestTest5Repository.Delete(x => x.TestId == input.Id);
}

来自 Cascade Delete 上的文档:

For optional relationships: ClientSetNull (default)
- Effect on dependent/child in database: None

您必须在 DbContext 中使用 Fluent API 配置删除行为:

modelBuilder.Entity<TestTest2>()
            .HasOne(t => t.Test)
            .WithMany()
            .OnDelete(DeleteBehavior.Cascade);