Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mixing sproc and non-sproc commands in the same batch may fail with a NullReferenceException #29643

Closed
maboivin opened this issue Nov 21, 2022 · 1 comment · Fixed by #29680 or #29722
Closed
Assignees
Labels
area-save-changes closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Milestone

Comments

@maboivin
Copy link

maboivin commented Nov 21, 2022

File a bug

Deleting a child entity when the parent entity uses DeleteUsingStoredProcedure fails with a NullReferenceException. It happens only when the child entity is already loaded in the entity tracker prior to deleting the parent entity.

Include your code

Repro project can be found here: https://github.com/maboivin/EFCoreIssueDeleteUsingStoredProcedure

using (var context = new ReproDbContext())
{
    context.Database.Migrate();

    var parent = new Parent() { Name = "parent" };
    context.Add(parent);
    context.SaveChanges();

    var child = new Child() { ParentId = parent.ParentId, Name = "child" };
    context.Add(child);
    context.SaveChanges();

    context.Remove(parent);

    // Since the parent entity is configured to be deleted with a stored procedure, it fails with a NullReferenceException.
    context.SaveChanges();
}

Include stack traces

Unhandled exception. Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while saving the entity changes. See the inner exception for details.
 ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.Consume(RelationalDataReader reader)
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.Consume(RelationalDataReader reader)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.SqlServer.Update.Internal.SqlServerModificationCommandBatch.Execute(IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChanges(IList`1 entries)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.<>c.<SaveChanges>b__107_0(DbContext _, ValueTuple`2 t)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChanges()
   at Program.<Main>$(String[] args) in C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\Program.cs:line 18

Include verbose output

SQL query executed when deleting the parent entity record:

declare @p4 int
set @p4=1
exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Child]
OUTPUT 1
WHERE [ChildId] = @p0;
EXEC @p1 = [DeleteParent] @p2;
',N'@p0 uniqueidentifier,@p1 int output,@p2 uniqueidentifier',@p0='99DD433E-F36B-1410-82A4-00F564887FD3',@p1=@p4 output,@p2='97DD433E-F36B-1410-82A4-00F564887FD3'
select @p4

Database contexts:

PM> Get-DbContext -verbose
Using project 'EFCoreIssueDeleteUsingStoredProcedure'.
Using startup project 'EFCoreIssueDeleteUsingStoredProcedure'.
Build started...
Build succeeded.
C:\Program Files\dotnet\dotnet.exe exec --depsfile C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\bin\Debug\net7.0\EFCoreIssueDeleteUsingStoredProcedure.deps.json --additionalprobingpath C:\Users\maboivin\.nuget\packages --runtimeconfig C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\bin\Debug\net7.0\EFCoreIssueDeleteUsingStoredProcedure.runtimeconfig.json C:\Users\maboivin\.nuget\packages\microsoft.entityframeworkcore.tools\7.0.0\tools\netcoreapp2.0\any\ef.dll dbcontext list --json --verbose --no-color --prefix-output --assembly C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\bin\Debug\net7.0\EFCoreIssueDeleteUsingStoredProcedure.dll --project C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure.csproj --startup-assembly C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\bin\Debug\net7.0\EFCoreIssueDeleteUsingStoredProcedure.dll --startup-project C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure.csproj --project-dir C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\ --language C# --configuration Debug --working-dir C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure --root-namespace EFCoreIssueDeleteUsingStoredProcedure --nullable
Using assembly 'EFCoreIssueDeleteUsingStoredProcedure'.
Using startup assembly 'EFCoreIssueDeleteUsingStoredProcedure'.
Using application base 'C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\bin\Debug\net7.0'.
Using working directory 'C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure'.
Using root namespace 'EFCoreIssueDeleteUsingStoredProcedure'.
Using project directory 'C:\Dev\GitHub\maboivin\EFCoreIssueDeleteUsingStoredProcedure\EFCoreIssueDeleteUsingStoredProcedure\'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider in assembly 'EFCoreIssueDeleteUsingStoredProcedure'...
Finding Microsoft.Extensions.Hosting service provider...
No static method 'CreateHostBuilder(string[])' was found on class 'Program'.
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'ReproDbContext'.

ReproDbContext

Include provider and version information

EF Core version: 7.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer 7.0.0
Target framework: .NET 7.0
Operating system: Windows 11 21H2 (Build 22000.1219)
IDE: Visual Studio 2022 17.4.0

@roji roji self-assigned this Nov 21, 2022
@ajcvickers ajcvickers added this to the 8.0.0 milestone Nov 23, 2022
roji added a commit to roji/efcore that referenced this issue Nov 25, 2022
@roji roji added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Nov 25, 2022
@roji
Copy link
Member

roji commented Nov 25, 2022

Thanks for the quality repro @maboivin! I've located the root cause and we'll likely patch 7.0 with the fix.

As a side note, you can replace this pattern in OnModelCreating:

var childBuilder = modelBuilder.Entity<Child>();
childBuilder.ToTable(nameof(Child));
childBuilder.HasKey(c => c.ChildId);
childBuilder.Property(c => c.ChildId).HasDefaultValueSql("NEWSEQUENTIALID()");
childBuilder.Property(c => c.Name).IsRequired();
childBuilder.HasOne(c => c.Parent).WithMany().HasForeignKey(c => c.ParentId);

... with the following, simpler one:

modelBuilder.Entity<Child>(b =>
{
    b.ToTable(nameof(Child));
    b.HasKey(c => c.ChildId);
    b.Property(c => c.ChildId).HasDefaultValueSql("NEWSEQUENTIALID()");
    b.Property(c => c.Name).IsRequired();
    b.HasOne(c => c.Parent).WithMany().HasForeignKey(c => c.ParentId);
});

@roji roji changed the title Deleting a child entity instance when the parent entity uses DeleteUsingStoredProcedure fails with a NullReferenceException. Mixing sproc and non-sproc commands in the same batch may fail with a NullReferenceException Nov 25, 2022
roji added a commit to roji/efcore that referenced this issue Dec 1, 2022
@roji roji reopened this Dec 1, 2022
@ajcvickers ajcvickers modified the milestones: 8.0.0, 7.0.3 Dec 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-save-changes closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported Servicing-approved type-bug
Projects
None yet
3 participants