Nella mia app utilizzo l'approccio code-first e sto aggiungendo entità a DbContext solo tramite IEntityTypeConfiguration<>
. Il mio obiettivo è quello di ottenere nomi di tabelle pluralizzati, ovvero modelli per modello .
Dopo aver letto la documentazione , articolo , questa domanda il mio understading sarebbe che il mio pluralizzatore dovrebbe essere registrato e come IPluralizer
utilizzato durante la creazione della migrazione, tuttavia non lo è.
Naturalmente potrei usare implicitamente DbSet<Model> Models {get;set;}
o usare builder.ToTable("Models");
, ma nel mio scenario generico vorrei evitarlo, soprattutto perché vorrei che alcuni modelli non ignorassero lo scenario generico astratto.
La domanda è, sto facendo qualcosa di sbagliato, o ho frainteso il modo in cui dovrebbe comportarsi
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);
}
}
Risultato
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Model",
columns: table => new
// code skipped for brevity
}
}
Risultato atteso:
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Models",
columns: table => new
// code skipped for brevity
}
}
L'articolo collegato non è corretto. Come si può vedere dal gancio di pluralizzazione per la documentazione di DbContext Scaffolding EF Core:
EF Core 2.0 introduce un nuovo servizio
IPluralizer
che viene utilizzato per singolarizzare i nomi dei tipi di entità e pluralizzare i nomiDbSet
.
In breve, viene utilizzato solo dai comandi di ponteggio, quindi non può essere utilizzato per modificare le convenzioni del modello del nome di tabella.
In generale, gli strumenti di migrazione utilizzano il modello nel modo in cui è configurato da convenzioni, annotazioni di dati e API fluenti. Quindi l'applicazione della convenzione personalizzata dovrebbe avvenire con l'API del modello all'interno di OnModelCreating
. Qualcosa come questo:
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
entityType.Relational().TableName = GetTableName(entityType);
dove il metodo GetTableName
implementa la convenzione di denominazione:
string GetTableName(IEntityType entityType)
{
// use entiityType.Name and other info
return ...;
}
Aggiornamento (EF Core 3.0+): utilizzare entityType.SetTableName(...)
al posto di entityType.Relational().TableName = ...