3

In my app I use code-first approach and I'm adding entities to DbContext only via IEntityTypeConfiguration<>. My goal is to achieve pluralized table names i.e. Models for Model.

After reading documentation, article, This question my understading would be that my pluralizer should be registered and as IPluralizer used during creating migration, however it is not.

Of course I could have implicitly use DbSet<Model> Models {get;set;} or use builder.ToTable("Models");, but in my generic scenario I'd like to avoid that, especially as I would like some models not to override abstract generic scenario.

Question is, am I doing something wrong, or I misunderstand the way it should behave

AppDesignService.cs

public class AppDesignService : IDesignTimeServices
{
    public void ConfigureDesignTimeServices(IServiceCollection services)
    {
        Debugger.Launch();
        services.AddSingleton<IPluralizer, InflectorPluralizer>();
    }
}

MyDbContext.cs

public class AppDbContext : IdentityDbContext<AppUser,AppRole,Guid>
{
    public EmsDbContext(DbContextOptions options) : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(AppDbContext)));
    }
}

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    var connectionString = Configuration.GetConnectionString("DefaultConnection");

    services.AddDbContext<DbContext, AppDbContext>(opt =>
     {
         opt.UseSqlServer(connectionString, sqlOpt =>
         {
             sqlOpt.EnableRetryOnFailure(3);
         });
     });

     // code skipped for brevity
}

Config

public interface IEntity
{
    int Id {get;set;}
}

public class Model : IEntity
{
    public int Id {get;set;}
    public string Name {get;set;}
}



public abstract class DbEntityConfig<T> : IEntityTypeConfiguration<T>
    where T : class, IEntity
{
    public void Configure(EntityTypeBuilder<T> builder)
    {
        builder.HasKey(m => m.Id);
    }
}


public class ModelEntityConfig : DbEntityConfig<Model>
{
    public void Configure(EntityTypeBuilder<Model> builder)
    {
        base.Configure(builder);

        // Of course I want to avoid this call, cos TheOtherModel might not override base method
        // builder.ToTable("Models");

        builder.Property(m => m.Name).HasMaxLength(25);
    }
}

Result

public partial class Test : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Model",
            columns: table => new

            // code skipped for brevity
    }
}

Expected result:

public partial class Test : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Models",
            columns: table => new

            // code skipped for brevity
    }
}
WueF
  • 450
  • 8
  • 15

1 Answers1

7

The linked article is incorrect. As you can see from Pluralization hook for DbContext Scaffolding EF Core documentation:

EF Core 2.0 introduces a new IPluralizer service that is used to singularize entity type names and pluralize DbSet names.

Shortly, it is used only by scaffolding commands, hence cannot be used for changing the table name model conventions.

In general migration tools use the model the way it is configured by conventions, data annotations and fluent API. So applying custom convention should be with model API inside OnModelCreating. Something like this:

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    entityType.Relational().TableName = GetTableName(entityType);

where GetTableName method implements your naming convention:

string GetTableName(IEntityType entityType)
{
    // use entiityType.Name and other info
    return ...; 
} 

Update (EF Core 3.0+): Use entityType.SetTableName(...) in place of entityType.Relational().TableName = ...

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343