EntityFrameworkCore.Triggered icon indicating copy to clipboard operation
EntityFrameworkCore.Triggered copied to clipboard

Accessing context.UnmodifiedEntity on entity with ComplexType crashes

Open DrPhil opened this issue 1 year ago • 5 comments

Accessing UnmodifiedEntity on a type with a ComplexType on it causes an exception. It doesn't matter if the changed value is inside the complextype or directly on the entity: accessing the unmodified value will crash anyway.

Example:

Models.cs

using EntityFrameworkCore.Triggered;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations.Schema;

namespace TriggeredComplexType;

public class MyContext : DbContext
{
    public DbSet<MyEntity> MyEntities { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        var folder = Environment.SpecialFolder.LocalApplicationData;
        var path = Environment.GetFolderPath(folder);
        options.UseSqlite($"Data Source={Path.Join(path, "complextypes.db")}");

        options.UseTriggers(triggerOptions =>
        {
            triggerOptions.AddTrigger(typeof(MyTrigger));
        });
    }
}

public class MyEntity
{
    public int Id { get; set; }
    public MyComplexType ComplexType { get; set; }
}

[ComplexType]
public class MyComplexType
{
    public string? Name { get; set; }
    public int Age { get; set; }
}

public class MyTrigger : IBeforeSaveTrigger<MyEntity>
{
    public async Task BeforeSave(ITriggerContext<MyEntity> context, CancellationToken cancellationToken)
    {
        Console.WriteLine("Here we are in the trigger");
        Console.WriteLine($"The unmodified entity is: {context.UnmodifiedEntity}");
    }
}

Program.cs

using TriggeredComplexType;

await using var context = new MyContext();

var myEntity = new MyEntity
{
    ComplexType = new MyComplexType
    {
        Name = "John",
        Age = 30
    }
};

Console.WriteLine("Adding to context");
context.Add(myEntity);
await context.SaveChangesAsync();


Console.WriteLine("Updating context");
myEntity.ComplexType.Name = "Jane";
await context.SaveChangesAsync();

csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="EntityFrameworkCore.Triggered" Version="3.2.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
  </ItemGroup>

</Project>

Output

Adding to context
Here we are in the trigger
The unmodified entity is: 
Updating context
Here we are in the trigger
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at lambda_method50(Closure, MaterializationContext)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ArrayPropertyValues.ToObject()
   at EntityFrameworkCore.Triggered.TriggerContext`1.get_UnmodifiedEntity()
   at TriggeredComplexType.MyTrigger.BeforeSave(ITriggerContext`1 context, CancellationToken cancellationToken) in /home/simon/Github/TriggeredComplexType/Models.cs:line 48
   at EntityFrameworkCore.Triggered.TriggerSession.RaiseTriggers(Type openTriggerType, Exception exception, ITriggerContextDiscoveryStrategy triggerContextDiscoveryStrategy, Func`2 triggerTypeDescriptorFactory, CancellationToken cancellationToken)
   at EntityFrameworkCore.Triggered.Internal.TriggerSessionSaveChangesInterceptor.SavingChangesAsync(DbContextEventData eventData, InterceptionResult`1 result, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in /home/simon/Github/TriggeredComplexType/Program.cs:line 24
   at Program.<Main>$(String[] args) in /home/simon/Github/TriggeredComplexType/Program.cs:line 24
   at Program.<Main>(String[] args)

DrPhil avatar Mar 04 '24 09:03 DrPhil

Can you try and use v4.0.0-preview.1? I couldn't reproduce this issue with that version

koenbeuk avatar Apr 21 '24 21:04 koenbeuk

I could reproduce this with 4.0.0-preview.1 as well.

Modified csproj

    <PackageReference Include="EntityFrameworkCore.Triggered" Version="4.0.0-preview.1" />

Modified Models.cs

public class MyTrigger : IBeforeSaveTrigger<MyEntity>
{
    public void BeforeSave(ITriggerContext<MyEntity> context)
    {
        Console.WriteLine("Here we are in the trigger");
        Console.WriteLine($"The unmodified entity is: {context.UnmodifiedEntity}");
    }
}

Output

Adding to context
Here we are in the trigger
The unmodified entity is: 
Updating context
Here we are in the trigger
Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at lambda_method50(Closure, MaterializationContext)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ArrayPropertyValues.ToObject()
   at EntityFrameworkCore.Triggered.TriggerContext`1.get_UnmodifiedEntity()
   at TriggeredComplexType.MyTrigger.BeforeSave(ITriggerContext`1 context) in /home/simon/Github/TriggeredComplexType/Models.cs:line 42
   at EntityFrameworkCore.Triggered.Internal.TriggerTypeDescriptorHelpers.<>c__DisplayClass2_0`2.<GetWeakDelegateHelper>b__0(Object trigger, Object triggerContext)
   at EntityFrameworkCore.Triggered.Internal.Descriptors.BeforeSaveTriggerDescriptor.Invoke(Object trigger, Object triggerContext, Exception exception)
   at EntityFrameworkCore.Triggered.Internal.TriggerDescriptor.Invoke(Object triggerContext, Exception exception)
   at EntityFrameworkCore.Triggered.TriggerSession.RaiseTriggers(Type openTriggerType, Exception exception, ITriggerContextDiscoveryStrategy triggerContextDiscoveryStrategy, Func`2 triggerTypeDescriptorFactory)
   at EntityFrameworkCore.Triggered.TriggerSession.RaiseBeforeSaveTriggers(Boolean skipDetectedChanges)
   at EntityFrameworkCore.Triggered.TriggerSession.RaiseBeforeSaveTriggers()
   at EntityFrameworkCore.Triggered.Internal.TriggerSessionSaveChangesInterceptor.SavingChangesAsync(DbContextEventData eventData, InterceptionResult`1 result, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Program.<Main>$(String[] args) in /home/simon/Github/TriggeredComplexType/Program.cs:line 21
   at Program.<Main>$(String[] args) in /home/simon/Github/TriggeredComplexType/Program.cs:line 21
   at Program.<Main>(String[] args)

I tried with the async version as well, and had the same result. I uploaded the example to a repository so hopefully you can reproduce as well. https://github.com/DrPhil/triggered-complextype-crash

DrPhil avatar Apr 23 '24 07:04 DrPhil

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 22 '24 12:06 stale[bot]

Is there anything else you need from me? Did my example in the repo reproduce for you?

DrPhil avatar Jun 28 '24 08:06 DrPhil

This issue seems to be resolved when targeting EF Core 9. I assume that there was an issue fixed related to this issue as the stacktrace points to something internal to EF Core. I'll leave this issue open for now as we may want to find a way to prevent this issue from happening when targeting EF Core 8 or below

koenbeuk avatar Aug 18 '24 23:08 koenbeuk

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Oct 30 '24 00:10 stale[bot]

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jan 06 '25 02:01 stale[bot]