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

IndexOutOfRange for EntityEntry.GetDatabaseValues() with complex type and TPH #32701

Closed
Wasserwecken opened this issue Jan 2, 2024 · 2 comments · Fixed by #32813
Closed

IndexOutOfRange for EntityEntry.GetDatabaseValues() with complex type and TPH #32701

Wasserwecken opened this issue Jan 2, 2024 · 2 comments · Fixed by #32813
Assignees
Labels
area-dbcontext 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

@Wasserwecken
Copy link

Wasserwecken commented Jan 2, 2024

Problem

I want to update the original values of an entity manually, therefore I use GetDatabaseValues.
Unfortunately in a specific edge case the indicies for IProperty on ArrayPropertyValues are partially invalid.
This results in an IndexOutOfRangeException because TPH properties indicies do not fit the underlying value array.
The root of the problem might be that

  • complex type values are not loaded
  • and the indicies TPH properties therfore do not match the value array length.

Not only does this result in an exception, this also:

  • affects EntityEntry.Reload() because it does the same internally.
  • complex property data does not get reloaded / loaded.

This problems only occures on TPH properties, there is no exception with normal entities.

Details

The value set loaded contains all proeprties except the complex type ones.
image

But the index of the additional property of the Supplier-Type does returns 4 with dbValues.Properties.ToArray()[3].GetIndex()
which results in an IndexOutOfRangeException

System.IndexOutOfRangeException
  HResult=0x80131508
  Message=Index was outside the bounds of the array.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.ArrayPropertyValues.get_Item(String propertyName)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntryPropertyValues.SetValues(PropertyValues propertyValues)
   at ConsoleApp2.Program.Main(String[] args) in C:\Users\ericd\source\repos\ConsoleApp2\ConsoleApp2\Program.cs:line 22

Example

The following code should reproduce the exception on the line entry.OriginalValues.SetValues(dbValues);.
Im using .NET 8.0 with EFCore 8.0.0 and VS Studio 17.8.3.
Before running the code, migrations have to be added (Add-Migration Init)

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

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            using (var ctx = new CustomContext())
            {
                ctx.Database.Migrate();
                ctx.Add(new Supplier() { Name = "FooBar", Address = new() { Street = "WhereEver" } });
                ctx.SaveChanges();
            }

            using (var ctx = new CustomContext())
            {
                var entry = ctx.Entry(ctx.Suppliers.First());
                var dbValues = entry.GetDatabaseValues()!;
                entry.OriginalValues.SetValues(dbValues);
            }

            Console.WriteLine("Done!");
            Console.ReadLine();
        }
    }
}

public class CustomContext : DbContext
{
    public DbSet<Contact> Contacts { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    public DbSet<Customer> Customers { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder builder)
    {
        builder.LogTo(Console.WriteLine, new[] { RelationalEventId.CommandExecuting }).EnableSensitiveDataLogging();
        builder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=TestDb;Trusted_Connection=True");
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Contact>().HasDiscriminator(e => e.Discriminator);
    }
}

public abstract class Contact
{
    public int Id { get; set; }
    public string? Discriminator { get; set; }
    public required string Name { get; set; }
    public required Address Address { get; set; }
}

public class Supplier : Contact
{
    public string? Foo { get; set; }
}

public class Customer : Contact
{
    public string? Bar { get; set; }
}

[ComplexType]
public class Address
{
    public required string Street { get; set; }
}
@ajcvickers
Copy link
Member

Note for triage: still repros on the latest daily. We should consider patching.

@Wasserwecken Thank you so much for posted a runnable, simple repro. It makes all the difference!

@ajcvickers ajcvickers removed this from the 8.0.x milestone Jan 4, 2024
@Wasserwecken
Copy link
Author

Wasserwecken commented Jan 10, 2024

Still happens in the release of 8.0.1. Heres a better code snipped to get to the origin of the exception:

            using (var ctx = new CustomContext())
            {
                var entry = ctx.Entry(ctx.Suppliers.First());
                var dbValues = entry.GetDatabaseValues()!;

                // This shows that some indicies are invalid
                foreach (var prop in dbValues.Properties)
                    Console.WriteLine(prop.GetIndex());

                // This fails
                entry.Reload();

                // This happens kind of internally in entry.Reload()
                entry.OriginalValues.SetValues(entry.GetDatabaseValues()!);

                // This happens kind of internally in OriginalValues.SetValues()
                // The exception occurs within the index accessor of dbValues
                foreach (var prop in dbValues.Properties)
                    entry.OriginalValues[prop] = dbValues[prop];
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-dbcontext 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
Development

Successfully merging a pull request may close this issue.

2 participants