11

I am new to ASP/EF. I am using ASP 5 and Entity Framework 7 in my personal project.

So I am able to create database and tables with code first approach, but all the table names are singular and does not pluralize by default.

In my ServerMatrixDemoDB DbContext file I have created below DBSets:

public class ServerMatrixDemoDB : DbContext
{
    public ServerMatrixDemoDB()
    {
        Database.EnsureCreated();
    }
    public DbSet<Domain> Domains { get; set; }
    public DbSet<Environment> Environments { get; set; }
    public DbSet<Network> Networks { get; set; }
    public DbSet<OsVersion> OsVersions { get; set; }
    public DbSet<Tier> Tiers { get; set; }
    public DbSet<Service> Services { get; set; }
    public DbSet<HardwareType> HardwareTypes { get; set; }
    public DbSet<Powershell> Powershell { get; set; }
    public DbSet<DotNetVersion> DotNetVersions { get; set; }
    public DbSet<Status> Status { get; set; }
    public DbSet<Servers> Servers { get; set; }
    public DbSet<Application> Applications { get; set; }

}

And in my startup.cs file I am using below code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()              // Add MVC Dependency.
        .AddJsonOptions(
            opt =>
            {
                opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // Api convert all property names to CamelCase.
            }
        );
    services.AddLogging();          // Enable Logging for database errors.
    services.AddEntityFramework()
      .AddSqlServer()
      .AddDbContext<ServerMatrixDemoDB>(options =>
      {
          options.UseSqlServer(@"Server=.\SQLEXPRESS;user id=sa;password='passwrd';Database=ServerMatrixDemoDB;integrated security=True;");
      });
    services.AddTransient<ServerMatrixDemoSeedData>();
}

Once I build and run the project, a database and tables get created, but all table names are singular. Is there a way to pluralize table names in code first approach?

Ray
  • 1,095
  • 3
  • 18
  • 43
  • I just tried this and my tables were named after their DbSet names. I had entities that did not have a DbSet and they ended up singular. Soon as I put them in as a DbSet with a plural name it was picked up in the migration as a table name change from the singular to the plural. Maybe this is something that has changed since the question was posted? – Joel McBeth Sep 11 '16 at 16:44

4 Answers4

15

you can do this in the OnModelCreating overload like -

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      foreach (var entity in modelBuilder.Model.GetEntityTypes())
      {
        modelBuilder.Entity(entity.Name).ToTable(entity.Name + "s");
      }
    }

you can also do this by using "data annotations"

    [Table("blogs")]
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
    }

or Fluent Api

class MyContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Blog>()
                .ToTable("blogs");
        }
    }

for more details have a look at - Documentation for EF7

Preet Singh
  • 1,791
  • 13
  • 16
  • I tried first and second solution and they both worked. :) I haven't tried the third one but I am sure it will work. Regarding first solution, I noticed that when the tables are created in database, it was naming `dbo.ServerMatrixDemoDb.Servers` instead of `dbo.servers`. The second solution does create `dbo.Servers`. – Ray Jan 10 '16 at 03:52
  • I mean it names `dbo.ServerMatrixDemo.Models.Servers` when I use the first solution. – Ray Jan 10 '16 at 04:52
  • in this call `modelBuilder.Entity().ToTable("blogs"); ` you can pass Schema name to the ToTable method. you can do the same in the !st solution mentioned in me answer – Preet Singh Jan 10 '16 at 12:09
  • How do the first and third solutions differ? Both are Fluent API based and both are executed upon model being created, aren't they? – Konrad Viltersten Aug 09 '16 at 20:11
  • @Konrad Viltersten The first one is using the fluent API to apply add an 's' to all the table names and the third one is using the fluent API to explicitly give a single table a plural name. – Joel McBeth Sep 11 '16 at 16:42
  • @jcmcbeth I was not specific enough. My question is how they differ **essentially**. If you set the name dynamically based on the name of the object or if you set the name as hard coded value, it's still the same **faulty** approach anyway. The former might be a bit more reusable, that's true. However, I prefer to use the built in pluralization of EF. Otherwise, we can't handle *success-sucesses*, *child-children*, *man-men* etc. – Konrad Viltersten Sep 11 '16 at 16:47
  • @KonradViltersten How is it faulty? If you have a better solution you should post it. – Joel McBeth Sep 11 '16 at 22:42
  • @jcmcbeth Perhaps it wasn't apparent in what I wrote. It's faulty (or at least sub-optimal in my opinion) because it's a hard coded name that needs maintenance and/or because the pluralization is incorrect. I prefer to use the built in pluralization of EF. Otherwise, we can't handle success-sucesses, child-children, man-men etc. If we for some incomprehensible reason prefer not to use plural names, then it can be turned off using the setup. Can't remember the name of the property though. It's so rarely used. – Konrad Viltersten Sep 12 '16 at 11:02
  • @KonradViltersten The issue here is that the built in pluralization isn't working. – Joel McBeth Sep 12 '16 at 14:02
  • @jcmcbeth I see... It could be something with the specific setup or some config that isn't listed here. I've only had problems with the pluralization when I started horsing around trying to fix *Human* to become *Humans* instead of *Humen*, as EF suggests (which is a bug). I noticed that you're not the OP - are you associated with his project or just a bystander? I'm asking because you might be able to give some extra insight in why it's not working. – Konrad Viltersten Sep 12 '16 at 15:29
  • @KonradViltersten No, just experiencing a similar issue and am wondering why the built in pluralization isn't working. – Joel McBeth Sep 12 '16 at 17:39
  • @jcmcbeth Interesting. It's something tricky, admittedly. Can you create a **brand new** solution/project, add two or three POCO classes and shove them into an **all new** DB? If you pick Azure demo account, it'll be even easier to reproduce the issue step-by-step for others. I've heard this story before and I have an idea why it might happen but I've never been able to reproduce it myself. – Konrad Viltersten Sep 12 '16 at 19:12
  • @KonradViltersten I think you should check out [this question](http://stackoverflow.com/questions/37493095/entity-framework-core-rc2-table-name-pluralization). In particular, this quote: _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._ There doesn't appear to be any built in pluralization in EF Core. – Joel McBeth Sep 13 '16 at 20:17
  • @jcmcbeth Please take a look [here](http://stackoverflow.com/a/17154539/1525840) and [here](http://stackoverflow.com/a/12133611/1525840). Are we talking about the same pluralization concept? It's been around even before Code First paradigm being introduced [like this](http://stackoverflow.com/a/7924806/1525840). – Konrad Viltersten Sep 14 '16 at 06:51
  • @KonradViltersten The question isn't about entity framework 5, it is about Entity Framework 7 which has been renamed Entity Framework Core. – Joel McBeth Sep 14 '16 at 13:47
  • @jcmcbeth You're absolutely right. I assumed that the functionality would be present in future versions of EF. If it isn't then I'll stand corrected. I don't have the 7th installed - would you be a sport and verify it for me on your system, please? (I'm assuming you're running v7, since you commented.) – Konrad Viltersten Sep 14 '16 at 14:46
  • @jcmcbeth How did it go? – Konrad Viltersten Sep 15 '16 at 14:05
  • Adding 's' at the end of the entity name may not work in all cases. For instance if you have an entity 'Methodology' the it will generate a table name 'Methodologys'. – polkduran Feb 24 '17 at 14:24
0

Current code convert tables, properties, keys and indexes to snake case for Postgre, you can use it as a base for your custom conventions:

using System;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
using Npgsql;

namespace Database.Customization
{
    public class PostgreDbContext : DbContext
    {
        private static readonly Regex _keysRegex = new Regex("^(PK|FK|IX)_", RegexOptions.Compiled);

        public PostgreDbContext(DbContextOptions options)
            : base(options)
        {
        }

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

        private void FixSnakeCaseNames(ModelBuilder modelBuilder)
        {
            var mapper = new NpgsqlSnakeCaseNameTranslator();
            foreach (var table in modelBuilder.Model.GetEntityTypes())
            {
                ConvertToSnake(mapper, table);
                foreach (var property in table.GetProperties())
                {
                    ConvertToSnake(mapper, property);
                }

                foreach (var primaryKey in table.GetKeys())
                {
                    ConvertToSnake(mapper, primaryKey);
                }

                foreach (var foreignKey in table.GetForeignKeys())
                {
                    ConvertToSnake(mapper, foreignKey);
                }

                foreach (var indexKey in table.GetIndexes())
                {
                    ConvertToSnake(mapper, indexKey);
                }
            }
        }

        private void ConvertToSnake(INpgsqlNameTranslator mapper, object entity)
        {
            switch (entity)
            {
                case IMutableEntityType table:
                    var relationalTable = table.Relational();
                    relationalTable.TableName = ConvertGeneralToSnake(mapper, relationalTable.TableName);
                    if (relationalTable.TableName.StartsWith("asp_net_"))
                    {
                        relationalTable.TableName = relationalTable.TableName.Replace("asp_net_", string.Empty);
                        relationalTable.Schema = "identity";
                    }

                    break;
                case IMutableProperty property:
                    property.Relational().ColumnName = ConvertGeneralToSnake(mapper, property.Relational().ColumnName);
                    break;
                case IMutableKey primaryKey:
                    primaryKey.Relational().Name = ConvertKeyToSnake(mapper, primaryKey.Relational().Name);
                    break;
                case IMutableForeignKey foreignKey:
                    foreignKey.Relational().Name = ConvertKeyToSnake(mapper, foreignKey.Relational().Name);
                    break;
                case IMutableIndex indexKey:
                    indexKey.Relational().Name = ConvertKeyToSnake(mapper, indexKey.Relational().Name);
                    break;
                default:
                    throw new NotImplementedException("Unexpected type was provided to snake case converter");
            }
        }

        private string ConvertKeyToSnake(INpgsqlNameTranslator mapper, string keyName) =>
            ConvertGeneralToSnake(mapper, _keysRegex.Replace(keyName, match => match.Value.ToLower()));

        private string ConvertGeneralToSnake(INpgsqlNameTranslator mapper, string entityName) =>
            mapper.TranslateMemberName(ModifyNameBeforeConvertion(mapper, entityName));

        protected virtual string ModifyNameBeforeConvertion(INpgsqlNameTranslator mapper, string entityName) => entityName;
    }
}
AuthorProxy
  • 7,946
  • 3
  • 27
  • 40
-1

One can also use a PluralizationService and create an attribute inheriting from the TableAttribute:

/// <summary>
/// Specifies the database table that a class is mapped to with pluralization.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "We want users to be able to extend this class")]
public class PluralizedTableAttribute : TableAttribute
{
    private const string _englishCultureName = "en-us";

    /// <summary>
    /// Initializes a new instance of the <see cref="PluralizedTableAttribute"/> class.
    /// </summary>
    /// <param name="name">The table name.</param>
    public PluralizedTableAttribute(string name)
        :base(PluralizationService.CreateService(CultureInfo.GetCultureInfo(_englishCultureName)).Pluralize(name)) // pluralization only suported in English.
    {
    }
}

And you'd use it just like AtbleAttribute except noweverything would be luralized

Dave
  • 349
  • 1
  • 15
-1
 private const string _englishCultureName = "en-us";

    public string GetPuralTableName(string singularTableName)
    {
        // pluralization only suported in English.
        return PluralizationService.CreateService(CultureInfo.GetCultureInfo(_englishCultureName)).Pluralize(singularTableName);

    }