Entity Framework Core RC2 table name pluralization
Asked Answered
S

8

69

Is there a way to do what this code did in EF Core RC 2?

protected override void OnModelCreating(ModelBuilder modelBuilder)
{    
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
Snubnosed answered 27/5, 2016 at 22:23 Comment(0)
I
134

There is no convention for this as of EF RC2 build. This is from EF Core team:

In past pre-release of EF Core, the table name for an entity was the same as the entity class name. In RC2 we now use the name of the DbSet property. If no DbSet property is defined for the given entity type, then the entity class name is used.


Now if you want to revert back to the RC1 naming conventions for tables, you have 3 ways to go with:


1. Choosing Singular Names for DbSet Properties:

One way is to singularize your DbSet property names (which I don't like). Say for example you have a Book entity and you want to map to a Book table:

public DbSet<Book> Book { get; set; }


2. Using ToTable() Fluent API:

You can of course always use fluent API to override any convention in place and dictate the table name to whatever you want:

modelBuilder.Entity<Book>().ToTable("Book");


3. Writing a Custom Convention:

Just because EF Core RC2 does not have a convention for this, it doesn't mean we can't write our own. To do so, first we need to create an extension method on ModelBuilder object:

using Microsoft.EntityFrameworkCore.Metadata.Internal;

public static class ModelBuilderExtensions 
{
    public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
    {
        foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
        {
            entity.Relational().TableName = entity.DisplayName();
        }
    }
}

And then we simply call it from the OnModelCreating method on our DbContext object:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.RemovePluralizingTableNameConvention();
}


On Closing:

I don't like plural table names and I like the last option better than the others and went with that. That said, it's my personal opinion and other developers might find any of these 3 ways more favorable than the others and choose to go with that :)

Impend answered 28/5, 2016 at 19:20 Comment(12)
Thank you Morteza. I think that for now I'll just set the table name on the entity.Snubnosed
I also added your way to my answer so that we have them all in one place as a point of reference for other fellow developers. Thanks.Impend
Morteza - I was actually referring to using the Table attribute on the poco class but I actually like your example #2 even better because that keeps all of the setup in one spot in the context as opposed to sprinkling the attributes on each poco.Snubnosed
If you're inheriting from IdentityDbContext, make the call to modelBuilder.RemovePluralizingTableNameConvention(); after base.OnModelCreating(builder) otherwise the AspNetUser tables will get mangled.Kissee
@MattMombrea In EF Core 2.0 calling modelBuilder.RemovePluralizingTableNameConvention(); after base.OnModelCreating(modelBuilder); does not solve problem when DbContext is inherited from IdentityDbContext. ASP Identity table names are still mangled.Orthopter
@Tančev Saša I tried to call modelBuilder.RemovePluralizingTableNameConvention(); before base.OnModelCreating(builder); and identity tables have OK namesBivouac
IMutableEntityType doesnt have Relational() method, is it changed?Darice
entity.Relational().TableName = entity.DisplayName(); not work in .net core 3Garnett
You should be able to use entity.SetTableName(entity.DisplayName());Sagittate
@MortezaManavi : Calling modelBuilder.RemovePluralizingTableNameConvention() causes issues when used in conjunction with entity.ToView("MyView"): The entity is then tagged as a table and not as a view anymore.Marker
The downside to option 3 is that it overrides EFCore.NamingConventions. Because of this, I've decided to just use the TableAttribute on all my entity classes and be totally explicit about what the name should be. Seems like the only way to get what I want (snake_case, singular).Aleuromancy
Continuing from my previous comment, I decided to stick with option 3 here, but also call another method to convert the entity.DisplayName() to snake case; entity.SetTableName(entity.DisplayName().ToSnakeCase());Aleuromancy
I
69

For EF Core 3.0 and above, use this to set the TableName property (because entity.Relational() no longer exist):

public static class ModelBuilderExtensions 
{
    public static void RemovePluralizingTableNameConvention(this ModelBuilder modelBuilder)
    {
        foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
        {
            if (entity.BaseType == null)
            {
                entity.SetTableName(entity.DisplayName());
            }
        }
    }
}
Isagogics answered 27/8, 2019 at 8:15 Comment(6)
If you use this method with an inherited entity, you'll get the error ...Only base type entity types can be mapped to a table.. To work around this, I used the condition if (entity.BaseType == null). Base classes will return null while inherited classes will obviously return the base type.Setaceous
@Isagogics : Calling modelBuilder.RemovePluralizingTableNameConvention() causes issues when used in conjunction with entity.ToView("MyView"): The entity is then tagged as a table and not as a view anymore.Marker
@Isagogics please check possible issues and workarouns for EF Core 5 https://mcmap.net/q/281549/-using-singular-table-names-with-ef-core-2Inject
How about .NET 6? Neither entity.Relational() nor entity.SetTableName() exists in .NET 6.Lifer
@Creative: entity.SetTableName exists in EF Core 6.Lemmueu
Please edit the if clause, throwed an error. Replace instead: if (entity.BaseType == null).Provencher
H
10

The EF Core version doesn't seem to support entity.DisplayName. This is a working alternative:

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    // Skip shadow types
    if (entityType.ClrType == null)
        continue;

    entityType.Relational().TableName = entityType.ClrType.Name;
}
Hamford answered 14/8, 2016 at 15:6 Comment(1)
entity.DisplayName is an extension method in the Microsoft.EntityFrameworkCore.Metadata.Internal namespace (which is actually public, not internal).Clobber
S
10

This is also an option:

 using System.ComponentModel.DataAnnotations.Schema;

 [Table("Book")]
 public class Book
 {...}

although you need to annotate it on each entity

Studner answered 26/4, 2019 at 19:24 Comment(1)
I found myself wanting to organize via schema, so I was annotating with this tag on every class anyway. As an Example: [Table("Book", Schema = "library")]Appleton
L
6

The EF Core 5 has resolved using a switch "-NoPluralize" while updating the Db Context:

Scaffold-DbContext "..conn str.." Microsoft.EntityFrameworkCore.SqlServer -OutputDir EntityDbContext -Project DoctorsExpress.Domain -Force -NoPluralize
Lesleelesley answered 29/1, 2021 at 10:41 Comment(1)
should be --no-pluralizeLiquidate
T
5

In Entity Framework NET core v2 you can choose to pluralize or singularize DbSets and Collections with a hook.

public class MyDesignTimeServices : IDesignTimeServices
{
    public void ConfigureDesignTimeServices(IServiceCollection services)
    {
        services.AddSingleton<IPluralizer, MyPluralizer>();
    }
}

public class MyPluralizer : IPluralizer
{
    public string Pluralize(string name)
    {
        return Inflector.Inflector.Pluralize(name) ?? name;
    }

    public string Singularize(string name)
    {
        return Inflector.Inflector.Singularize(name) ?? name;
    }
}

See this answer for more information: https://mcmap.net/q/281550/-entityframework-core-database-first-approach-pluralizing-table-names

Tormentil answered 21/11, 2017 at 10:32 Comment(0)
P
0

I use this for EF Core 3.1 to preserve [Table("some_table_name")] annotation on entity types, although ConventionAnnotation is an internal class.

static class UseEntityTypeNameForTableNameExtension
{
    public static void UseEntityTypeNameForTableName(this ModelBuilder modelBuilder)
    {
        foreach (var entity in modelBuilder.Model.GetEntityTypes())
        {
            var tableNameAnnotation = (ConventionAnnotation)entity.FindAnnotation(RelationalAnnotationNames.TableName);
#pragma warning disable EF1001
            var configurationSource = tableNameAnnotation.GetConfigurationSource();
#pragma warning restore EF1001
            if (configurationSource != ConfigurationSource.Convention)
            {
                // Set explicitly using Fluent API or has TableAttribute DataAnnotation
                continue;
            }
            var defaultTableName = entity.GetDefaultTableName();
            entity.SetTableName(defaultTableName);
        }
    }
}
Pinetum answered 7/5, 2020 at 10:38 Comment(0)
A
-2

I went with adding a virtual

public virtual DbSet<TableName> TableName{ get; set; }
public DbSet<TableName> TableNames { get; set; }

TableNames.FirstOrDefault();
Alchemize answered 8/7, 2020 at 23:16 Comment(1)
Quality username. Low quality answer. You should mention why you went with this approach and how it solves the problem in the description.Aleuromancy

© 2022 - 2024 — McMap. All rights reserved.