0

I'm trying to implement hard delete for FullyAuditedEntity Entity Test. The primary key of Test is Id, which is being referred to a TestTest2 entity as a foreign key. When I'm trying to delete a record from Test entity, it gives the below error.

I have followed this question for implementation.

TestAppService

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

TestRepository

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

TestTest2

[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

Note: It should delete the entry from Test and Test2 tables.

Update

The below anwer works pretty well but I have some specific requirement. Test Entity Id is being referred in other tables like TestTest2, TestTest3, TestTest4, TestTest5. When I delete a record from Test table, it should delete from all tables. But I also need to call the other dependent tables ( for example TestTest2, TestTest3, TestTest4, TestTest5) Delete method to do some extra cleanup specific to that entity (for example TestTest2, TestTest3, TestTest4, TestTest5).

TestRepository

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);
}
Vivek Nuna
  • 25,472
  • 25
  • 109
  • 197
  • It's hard to follow your string based configuration, but looks like you forgot to mark `TestTest2.TestId` property as required, thus by convention EFC does not set cascade delete on. – Ivan Stoev Feb 28 '18 at 18:49

1 Answers1

2

From the documentation on Cascade Delete:

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

You have to configure the delete behavior using Fluent API in your DbContext:

modelBuilder.Entity<TestTest2>()
            .HasOne(t => t.Test)
            .WithMany()
            .OnDelete(DeleteBehavior.Cascade);
aaron
  • 39,695
  • 6
  • 46
  • 102