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

NRE when adding migrations to version 6.0.0-preview.5 #1491

Closed
war-beast opened this issue Aug 9, 2021 · 10 comments
Closed

NRE when adding migrations to version 6.0.0-preview.5 #1491

war-beast opened this issue Aug 9, 2021 · 10 comments

Comments

@war-beast
Copy link

war-beast commented Aug 9, 2021

We have migrated our project from Core 2.2 to .NET5. We also updated Pomelo.EntityFrameworkCore.MySql to version 6.0.0-preview.5

When adding a migration, whether it is empty or with changes to the database model, the exception Object reference not set to an instance of an object. in 2 places in the file inherited from ModelSnapshot:

  1. Inside the string b.Property<string[]>("Values")
modelBuilder.Entity("Entities.WaybillAutocompleteRule", b =>
{
    b.Property<Guid>("Id").ValueGeneratedOnAdd();

    b.Property<Guid>("AutocompleteId");

    b.Property<string>("Key").IsRequired();

    b.Property<int>("Type");

    b.Property<string[]>("Values").IsRequired();

    b.HasKey("Id");

    b.HasIndex("AutocompleteId");

    b.ToTable("waybill_autocomplete_rules");
});

There is a transformation in the configuration of this property:
builder.Property(e => e.Values).HasConversion(x => string.Join(';', x), x => x.Split(';', StringSplitOptions.None));

The second place of the same exception, in the line with b.Property<double>("Count")

modelBuilder.Entity("Entities.RegistryOrderServices", b =>
{
    b.Property<Guid>("Id").ValueGeneratedOnAdd();

    b.Property<double>("Count");

    b.Property<decimal>("Price");

    b.Property<Guid>("RegistryOrderId");

    b.Property<int>("Type");

    b.HasKey("Id");

    b.HasIndex("RegistryOrderId");

    b.ToTable("registry_order_services");
});

In the model, the type of the Values ​​property is IEnumerable<string> (but the database stores a string separated by ";") and the type of the Count property is double.

If I delete the ModelSnapshot file and add a migration, a new snapshot file is generated and the migration is created, with exactly the same lines. But when I try to delete this migration or add 1 more, I get exactly the same exceptions.

@mguinness mguinness changed the title NRE when adding migrations to Pomelo.EntityFrameworkCore.MySql version 6.0.0-preview.5 NRE when adding migrations to version 6.0.0-preview.5 Aug 9, 2021
@mguinness
Copy link
Collaborator

If you're using .NET5 you should be using Pomelo 5.0.1 which is the latest stable release.

You haven't provided the models for either WaybillAutocompleteRule or RegistryOrderServices and we'll need the full DbContext and stack trace that was requested in the new issue template.

Also see Entity Framework Core 2.1 Add Migration System.NullReferenceException for some steps to try,

@war-beast
Copy link
Author

war-beast commented Aug 9, 2021

Ok I'll try version 5.0.1

On the current issue.

public class RegistryOrderServices
{
    public Guid Id { get; set; }

    public RegistryOrderServicesType Type { get; set; }
    public double Count { get; set; }
    public decimal Price { get; set; }

    public Guid RegistryOrderId { get; set; }
    public RegistryOrder RegistryOrder { get; set; }
}

public class WaybillAutocompleteRule
{
    public Guid Id { get; set; }

    public string Key { get; set; }
    public WaybillAutocompleteRuleType Type { get; set; }
    public IEnumerable<string> Values { get; set; }

    public Guid AutocompleteId { get; set; }
    public WaybillAutocomplete Autocomplete { get; set; }
}

DBContext:

public class Store : MigratorDbContext
{
    public Store(DbContextOptions options) : base(options) { }

    public DbSet<RegistryOrderServices> RegistryOrderServices { get; set; }
    public DbSet<WaybillAutocompleteRule> WaybillAutocompleteRules { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(Store).Assembly);
    }

    public override void Dispose()
    {
        base.Dispose();
    }
}

public abstract class MigratorDbContext : DbContext
{
    public MigratorDbContext(DbContextOptions options) : base(options) { }

    public DbSet<MigrationEntity> Migrations { get; set; }
}

Other DbSets are irrelevant (I will be punished for posting the full text).

Stack trace:

System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder.Property[TProperty](String propertyName)
   at Entities.Persistence.Migrations.StoreModelSnapshot.<>c.<BuildModel>b__0_23(EntityTypeBuilder b) in D:\work\Solutionname\Entities.Persistence\Migrations\StoreModelSnapshot.cs:line 1018
   at Microsoft.EntityFrameworkCore.ModelBuilder.Entity(String name, Action`1 buildAction)
   at Entities.Persistence.Migrations.StoreModelSnapshot.BuildModel(ModelBuilder modelBuilder) in D:\work\Solutionname\Entities.Persistence\Migrations\StoreModelSnapshot.cs:line 1012
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot.CreateModel()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot.get_Model()
   at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Object reference not set to an instance of an object.

I tried the method provided by the link on SO, did not help, I get the same error.

@mguinness
Copy link
Collaborator

What code is in lines 1012 and 1018 of StoreModelSnapshot.cs? Also see EF Core 2.1 project Add-Migration NullReferenceException.

@lauxjpn
Copy link
Collaborator

lauxjpn commented Aug 9, 2021

Pomelo 6.0.0-preview.5 is compatible with .NET 5+ (as long as you are using EF Core 6.0.0-preview.5 as well), so that should not be the issue.


In addition to what @mguinness said, please post your Fluent API configuration for the RegistryOrderServices and WaybillAutocompleteRule entities.

Also make sure that your dotnet-ef tool version corresponds to the version you have referenced in your project:

dotnet ef --version

@war-beast
Copy link
Author

What code is in lines 1012 and 1018 of StoreModelSnapshot.cs?

1012        modelBuilder.Entity("Entities.RegistryOrderServices", b =>
1013               {
1014                    b.Property<Guid>("Id")
1015                        .ValueGeneratedOnAdd()
1016                        .HasColumnType("char(36)");
1017
1018                    b.Property<double>("Count")
                        .HasColumnType("double");

                    b.Property<decimal>("Price")
                        .HasColumnType("decimal(65,30)");

Also see EF Core 2.1 project Add-Migration NullReferenceException.

I tried this method and it did not help, I get the same error.

On version 5.0.1, everything works well, migrations are added and removed without errors.

@war-beast
Copy link
Author

war-beast commented Aug 10, 2021

In addition to what @mguinness said, please post your Fluent API configuration for the RegistryOrderServices and WaybillAutocompleteRule entities.

public class RegistryOrderServicesConfig : IEntityTypeConfiguration<RegistryOrderServices>
{
    public void Configure(EntityTypeBuilder<RegistryOrderServices> builder)
    {
        builder.ToTable("registry_order_services");

        builder.HasKey(e => e.Id);

        builder.Property(e => e.Type).IsRequired();

        builder.Property(e => e.RegistryOrderId).IsRequired();
    }
}

public class WaybillAutocompleteRuleConfig : IEntityTypeConfiguration<WaybillAutocompleteRule>
{
    public void Configure(EntityTypeBuilder<WaybillAutocompleteRule> builder)
    {
        builder.ToTable("waybill_autocomplete_rules");

        builder.Property(e => e.Key).IsRequired();
        builder.Property(e => e.Type).IsRequired();
        builder.Property(e => e.Values).IsRequired();

        builder.HasOne(e => e.Autocomplete)
            .WithMany(e => e.Rules)
            .HasForeignKey(e => e.AutocompleteId)
            .IsRequired();

        builder.Property(e => e.Values)
            .HasConversion(x => string.Join(';', x), x => x.Split(';', StringSplitOptions.None));
    }
}

Also make sure that your dotnet-ef tool version corresponds to the version you have referenced in your project:

dotnet ef --version

Entity Framework Core .NET Command-line Tools
5.0.8

The .cproj file contains a preview version:
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.0-preview.5.21301.9">

@lauxjpn lauxjpn self-assigned this Aug 10, 2021
@lauxjpn
Copy link
Collaborator

lauxjpn commented Aug 10, 2021

@war-beast I am able to reproduce this.

This seems to be a bug in EF Core that is only triggered for specific entity property names (e.g. when your property is named Count) in migration snapshots.

The underlying issue seems to be, that EF Core is checking the properties of the wrong type. It does enumerated the properties of the Metadata.ClrType property, but it is not set to the actual entity type (since this is a migration snapshot), but to a type of Dictionary<string, object>.
It then hits the Count property of this Dictionary<string, object>, checks its int type against the expected double type of the entity's property, realizes that they do not match, and finally returns null, which will then throws the NRE.

This is the line with the issue:

https://github.com/dotnet/efcore/blob/159c7e728b29cef1a8208e69d15eb058eae0c4d8/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs#L579

memberInfo ??= Metadata.ClrType.GetMembersInHierarchy(propertyName).FirstOrDefault();

@AndriySvyryd, @ajcvickers Reproduced with the following code:

Program.cs
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class RegistryOrderServices
    {
        public Guid Id { get; set; }
        public double Count { get; set; }
    }

    public class Context : DbContext
    {
        public DbSet<RegistryOrderServices> RegistryOrderServices { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                var connectionString = "server=127.0.0.1;port=3308;user=root;password=;database=Issue1491";
                var serverVersion = ServerVersion.AutoDetect(connectionString);

                optionsBuilder.UseMySql(connectionString, serverVersion)
                    .UseLoggerFactory(
                        LoggerFactory.Create(
                            b => b
                                .AddConsole()
                                .AddFilter(level => level >= LogLevel.Information)))
                    .EnableSensitiveDataLogging()
                    .EnableDetailedErrors();
            }
        }
    }

    internal static class Program
    {
        private static void Main()
        {
        }
    }
}

Add an initial migration and then another one (can be empty).
Should be reproducible with any provider.


@war-beast If you want to use the previews, then the quickes workaround is probably to just rename your property to something different than Count for now (e.g. OrderCount), until this issue has been fixed (you might have to manually modify the latest migration files for this to work properly).

@war-beast
Copy link
Author

war-beast commented Aug 11, 2021

@lauxjpn thank you ever so much!

If you want to use the previews, then the quickes workaround is probably to just rename your property to something different than Count for now (e.g. OrderCount), until this issue has been fixed (you might have to manually modify the latest migration files for this to work properly).

It will be difficult to do, too much of everything is tied to this data. I will wait for the solution of this issue.
While version 5.0.1 has been installed, there is no such error in it.

This seems to be a bug in EF Core that is only triggered for specific entity property names (e.g. when your property is named Count) in migration snapshots.

As I understand it, does the same thing happen with a property named "Values"?

@lauxjpn
Copy link
Collaborator

lauxjpn commented Aug 12, 2021

As I understand it, does the same thing happen with a property named "Values"?

I would expect it to suffer from the same issue.

@lauxjpn
Copy link
Collaborator

lauxjpn commented Sep 6, 2021

This has been fixed in EF Core upstream and will be part of EF Core 6.0 RC2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants