Skip to content

ReadOnlyCollection<string> with private backing field throws exception on NullabilityInfoContext when trying to add migration #34996

Closed

Description

@Red-F

Adding a Migration throws 'System.InvalidOperationException'

After adding a new public property with a private backing field to the model, the create and drop apis (context.Database.EnsureDeleted()/context.Database.EnsureCreated()) produce the expected database and table. But trying to add a new migration throws the following:

dotnet ef migrations add SampleMigration
Build started...
Build succeeded.
System.InvalidOperationException: Cannot scaffold C# literals of type 'System.Reflection.NullabilityInfoContext'. The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.UnknownLiteral(Object value)
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.<Fragment>g__AppendMethodCall|57_0(IMethodCallCodeFragment current, <>c__DisplayClass57_0&)
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.Fragment(IMethodCallCodeFragment fragment, Int32 indent)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateAnnotations(String builderName, IAnnotatable annotatable, IndentedStringBuilder stringBuilder, Dictionary`2 annotations, Boolean inChainedCall, Boolean leadingNewline, MethodInfo hasAnnotationMethodInfo)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.Generate(String modelBuilderName, IModel model, IndentedStringBuilder stringBuilder)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateMetadata(String migrationNamespace, Type contextType, String migrationName, String migrationId, IModel targetModel)
   at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language, Boolean dryRun)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace, Boolean dryRun)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace, Boolean dryRun)
   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)
Cannot scaffold C# literals of type 'System.Reflection.NullabilityInfoContext'. The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.

Sample code

Please find the minimum code project here:

https://github.com/Red-F/EfCoreMigrationIssue

This was code added to the model

    public IReadOnlyCollection<string> Uids => _uids.ToList();
    private List<string> _uids = [];
    
    public void AddUids(IEnumerable<string> uids)
    {
        _uids.AddRange(uids);
    }
    
    public void RemoveUids(IEnumerable<string> uids)
    {
        foreach (var uid in uids)
        {
            _uids.Remove(uid);
        }
    }

Stack traces

See above.

Verbose output

dotnet ef --verbose migrations add SampleMigration
Using project '/home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj'.
Using startup project '/home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj'.
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=/tmp/tmpw9FrlF.tmp /verbosity:quiet /nologo /home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=/tmp/tmp0ai4Yq.tmp /verbosity:quiet /nologo /home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj
Build started...
dotnet build /home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj /verbosity:quiet /nologo /p:PublishAot=false

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.88

Build succeeded.
dotnet exec --depsfile /home/fred/projects/EfCoreMigrationIssue/bin/Debug/net8.0/EfCoreMigrationIssue.deps.json --additionalprobingpath /home/fred/.nuget/packages --runtimeconfig /home/fred/projects/EfCoreMigrationIssue/bin/Debug/net8.0/EfCoreMigrationIssue.runtimeconfig.json /home/fred/.dotnet/tools/.store/dotnet-ef/9.0.0-rc.1.24451.1/dotnet-ef/9.0.0-rc.1.24451.1/tools/net8.0/any/tools/netcoreapp2.0/any/ef.dll migrations add SampleMigration --assembly /home/fred/projects/EfCoreMigrationIssue/bin/Debug/net8.0/EfCoreMigrationIssue.dll --project /home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj --startup-assembly /home/fred/projects/EfCoreMigrationIssue/bin/Debug/net8.0/EfCoreMigrationIssue.dll --startup-project /home/fred/projects/EfCoreMigrationIssue/EfCoreMigrationIssue.csproj --project-dir /home/fred/projects/EfCoreMigrationIssue/ --root-namespace EfCoreMigrationIssue --language C# --framework net8.0 --nullable --working-dir /home/fred/projects/EfCoreMigrationIssue --verbose
Using assembly 'EfCoreMigrationIssue'.
Using startup assembly 'EfCoreMigrationIssue'.
Using application base '/home/fred/projects/EfCoreMigrationIssue/bin/Debug/net8.0'.
Using working directory '/home/fred/projects/EfCoreMigrationIssue'.
Using root namespace 'EfCoreMigrationIssue'.
Using project directory '/home/fred/projects/EfCoreMigrationIssue/'.
Remaining arguments: .
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding DbContext classes in the project...
Found DbContext 'DemoDbContext'.
Finding application service provider in assembly 'EfCoreMigrationIssue'...
Finding Microsoft.Extensions.Hosting service provider...
Using environment 'Development'.
Using application service provider from Microsoft.Extensions.Hosting.
Using context 'DemoDbContext'.
Finding design-time services referenced by assembly 'EfCoreMigrationIssue'...
Finding design-time services referenced by assembly 'EfCoreMigrationIssue'...
No referenced design-time services were found.
Finding design-time services for provider 'Microsoft.EntityFrameworkCore.SqlServer'...
Using design-time services from provider 'Microsoft.EntityFrameworkCore.SqlServer'.
Finding IDesignTimeServices implementations in assembly 'EfCoreMigrationIssue'...
No design-time services were found.
System.InvalidOperationException: Cannot scaffold C# literals of type 'System.Reflection.NullabilityInfoContext'. The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.UnknownLiteral(Object value)
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.<Fragment>g__AppendMethodCall|57_0(IMethodCallCodeFragment current, <>c__DisplayClass57_0&)
   at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.Fragment(IMethodCallCodeFragment fragment, Int32 indent)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateAnnotations(String builderName, IAnnotatable annotatable, IndentedStringBuilder stringBuilder, Dictionary`2 annotations, Boolean inChainedCall, Boolean leadingNewline, MethodInfo hasAnnotationMethodInfo)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.Generate(String modelBuilderName, IModel model, IndentedStringBuilder stringBuilder)
   at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateMetadata(String migrationNamespace, Type contextType, String migrationName, String migrationId, IModel targetModel)
   at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language, Boolean dryRun)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace, Boolean dryRun)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace, Boolean dryRun)
   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)
Cannot scaffold C# literals of type 'System.Reflection.NullabilityInfoContext'. The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.```

### Include provider and version information

EF Core version: 9.0.0-rc.2.24473.5 
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 8.0
Operating system: Linux
IDE: Rider/CLI

Activity

  1. self-assigned this
    on Oct 30, 2024
  2. ajcvickers commented on Oct 30, 2024

    @ajcvickers
    Contributor

    I was able to repro this with the following code:

    public class Model
    {
        public int Id { get; set; }
        public IReadOnlyCollection<string> Uids => _uids.ToList();
        private List<string> _uids = [];
    }
    
    public class DemoDbContext : DbContext
    {
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Model>().Property(x => x.Uids).HasField("_uids");
        }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
            => optionsBuilder.UseSqlServer();
    }

    Note the use of Property for a primitive collection. I'm not sure if this should even be allowed; we should discuss. If PrimitiveCollection is used, then the error goes away:

    modelBuilder.Entity<Model>().PrimitiveCollection(x => x.Uids).HasField("_uids");

    @Red-F Is there a reason for using Property here instead of PrimitiveCollection? (I recognize it's not clear that PrimitiveCollection should be used here.)

  3. added a commit that references this issue on Oct 30, 2024
    0c31e6c
  4. added this to the 10.0.0 milestone on Oct 30, 2024
  5. added 2 commits that reference this issue on Oct 30, 2024
    1840961
    121c280
  6. Red-F commented on Oct 31, 2024

    @Red-F
    Author

    @ajcvickers Why do we do the things that we do? A long time ago I saw a video where you discussed/explained this 'defensive copy' pattern. I liked that. A lot, because I think we need to make it as difficult as possible for ourselves to unintentionally make mistakes. So I use IReadOnlyCollection properties with a backing field of List everywhere.

    SInce I like my code to be predictable for future me or new readers, that means I use IReadOnlyCollection<string> as well. Just to have no surprises for readers/reviewers.

    I guess the same applies to the model configuration. Just predictably repeated configuration for all the collections. And to be honest, I also was not aware of .PrimitiveCollection(x => x.property). Even after reading your comment it took some effort to find it!

    I was able to get it working using a workaround like

            builder.Property(x => x.Uids).HasField("_uids")
                .HasConversion(
                    v => JsonConvert.SerializeObject(v, (JsonSerializerSettings?)null),
                    v => JsonConvert.DeserializeObject<List<string>>(v, (JsonSerializerSettings?)null)!,
                    ReadOnlyCollectionValueComparer<string>.Create());

    but my extensive reading of the online documentation (age will teach a person that reading documentation is a GOOD thing) had me convinced that the original code should work. In addition, the create and drop apis do work as intended, it is just that I can't create a migration (bundle).

  7. added 2 commits that reference this issue on Nov 11, 2024
    a558138
    a951dc8
  8. 14 remaining items

  9. added a commit that references this issue on Dec 19, 2024
    cd6a9ca
  10. removed their assignment
    on Dec 19, 2024
  11. bbascarevic commented on Jan 6, 2025

    @bbascarevic

    Hi @roji, any rough estimate on when may Design 9.0.2 land? This issue is keeping us from upgrading to EF 9.

  12. added a commit that references this issue on Jan 7, 2025
    cc16006
  13. modified the milestones: 9.0.x, 9.0.2 on Jan 14, 2025
  14. roji commented on Jan 16, 2025

    @roji
    Member

    @bbascarevic mid-Feb.

  15. added theissue type on Jun 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions