Skip to content

[Update documentation] Throw an exception when generating compiled model code that attempts to reference private methods #35033

@frankbuckley

Description

@frankbuckley

Given a value converter such as:

public sealed class BooleanToCharConverter : ValueConverter<bool, char>
{
    public static readonly BooleanToCharConverter Default = new();

    public BooleanToCharConverter()
        : base(v => ConvertToChar(v), v => ConvertToBoolean(v))
    {
    }

    private static char ConvertToChar(bool value)
    {
        return value ? 'Y' : 'N';
    }

    private static bool ConvertToBoolean(char value)
    {
        return value == 'Y';
    }
}

...generating a compiled model produces code that attempts to call the private conversion methods resulting in CS0122 compilation errors:

V:\dev\frankbuckley\ef-core-compiled-value-converter\Model\CompiledModels\SentenceEntityType.cs(42,57): error CS0122: 'BooleanToCharConverter.ConvertToChar(bool)' is inaccessible due to its protection level
V:\dev\frankbuckley\ef-core-compiled-value-converter\Model\CompiledModels\SentenceEntityType.cs(43,57): error CS0122: 'BooleanToCharConverter.ConvertToBoolean(char)' is inaccessible due to its protection level

Repro

using Microsoft.EntityFrameworkCore;
using Model;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

using var context = new LanguageDbContext();

context.Database.EnsureDeleted();
context.Database.EnsureCreated();

context.Sentences.Add(new Sentence { Text = "Hello, World!", IsPhrase = false });

context.SaveChanges();

var sentences = context.Sentences.ToList();

foreach (var sentence in sentences)
{
    Console.WriteLine(sentence.Text);
}


namespace Model
{
    public class Sentence
    {
        public int Id { get; set; }

        public required string Text { get; set; }

        public bool IsPhrase { get; set; }
    }

    public class LanguageDbContext : DbContext
    {
        public DbSet<Sentence> Sentences { get; set; } = default!;

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            _ = optionsBuilder.UseSqlite("DataSource=temp.db");
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Sentence>().Property(e => e.IsPhrase).HasConversion(Storage.BooleanToCharConverter.Default);
        }
    }
}

namespace Storage
{
    public sealed class BooleanToCharConverter : ValueConverter<bool, char>
    {
        public static readonly BooleanToCharConverter Default = new();

        public BooleanToCharConverter()
            : base(v => ConvertToChar(v), v => ConvertToBoolean(v))
        {
        }

        private static char ConvertToChar(bool value)
        {
            return value ? 'Y' : 'N';
        }

        private static bool ConvertToBoolean(char value)
        {
            return value == 'Y';
        }
    }
}
  • Build
  • Run dotnet ef dbcontext optimize
  • Build again (failes)

It would be preferable if the model compiler errors when referencing a private method rather than producing invalid code.

Workaround

  • Make methods public (if you can)

Metadata

Metadata

Assignees

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions