【问题标题】:Entity Framework 7 pluralize table names with code first approachEntity Framework 7 使用代码优先方法将表名复数
【发布时间】:2016-04-14 12:10:41
【问题描述】:

我是 ASP/EF 的新手。我在我的个人项目中使用 ASP 5 和 Entity Framework 7。

所以我可以使用代码优先的方法创建数据库和表,但所有表名都是单数的,默认情况下不会复数。

在我在 DBSet 下创建的 ServerMatrixDemoDB DbContext 文件中:

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; }

}

在我的 startup.cs 文件中,我使用以下代码:

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>();
}

一旦我构建并运行项目,就会创建一个数据库和表,但所有表名都是单数。有没有办法在代码优先的方法中复数表名?

【问题讨论】:

  • 我刚试过这个,我的表是以它们的 DbSet 名称命名的。我有没有 DbSet 的实体,它们最终是单数的。当我将它们作为具有复数名称的 DbSet 放入时,它在迁移中被拾取,因为表名称从单数变为复数。也许自从问题发布以来这已经发生了变化?

标签: c# asp.net entity-framework entity-framework-core


【解决方案1】:

您可以在 OnModelCreating 重载中执行此操作,例如 -

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

您也可以使用“数据注释”来做到这一点

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

或 Fluent Api

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

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

更多详情请查看 - Documentation for EF7

【讨论】:

  • 我尝试了第一个和第二个解决方案,它们都有效。 :) 我还没有尝试过第三个,但我相信它会起作用。关于第一个解决方案,我注意到在数据库中创建表时,它命名为dbo.ServerMatrixDemoDb.Servers 而不是dbo.servers。第二个解决方案确实创建了dbo.Servers
  • 我的意思是当我使用第一个解决方案时它命名为dbo.ServerMatrixDemo.Models.Servers
  • 在此调用modelBuilder.Entity&lt;Blog&gt;().ToTable("blogs"); 中,您可以将架构名称传递给 ToTable 方法。你可以在我回答中提到的 !st 解决方案中做同样的事情
  • 第一种和第三种解决方案有何不同?两者都是基于 Fluent API 的,并且都是在创建模型时执行的,不是吗?
  • @Konrad Viltersten 第一个是使用 fluent API 为所有表名添加一个“s”,第三个是使用 fluent API 显式地为单个表指定复数名称。
【解决方案2】:

当前代码将tablespropertieskeysindexes 转换为Postgre 的snake case,您可以将其用作自定义约定的基础:

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;
    }
}

【讨论】:

    【解决方案3】:

    也可以使用 PluralizationService 并创建一个继承自 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.
        {
        }
    }
    

    你可以像使用 AtbleAttribute 一样使用它,除了现在一切都会被 luralized

    【讨论】:

    • PluralizationService 未在 EF7 中实现。
    【解决方案4】:
     private const string _englishCultureName = "en-us";
    
        public string GetPuralTableName(string singularTableName)
        {
            // pluralization only suported in English.
            return PluralizationService.CreateService(CultureInfo.GetCultureInfo(_englishCultureName)).Pluralize(singularTableName);
    
        }
    

    【讨论】:

    • 应该在哪里调用这个GetPuralTableName?另外,请参阅提到PluralizationService的其他答案。
    • 你可以在任何地方调用它,例如你的控制器是这样的: var result=GetPuralTableName("your table string name")
    • 是的,如何使用它来获取复数表名?您可以使用 EF 核心呈现工作代码吗?
    猜你喜欢
    • 1970-01-01
    • 2016-11-03
    • 2012-08-22
    • 2013-03-26
    • 2019-08-02
    • 2020-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多