更改所有实体的标识列的名称

时间:2013-05-07 16:11:01

标签: entity-framework entity-framework-6

我正在创建域模型,并希望拥有一个带有“Id”属性的“BaseEntity”类(以及其他一些审计跟踪内容)。 Id属性是主键,我的域模型中的每个实体都将从BaseEntity类继承。相当简单的东西......

public class BaseEntity
{
    [Key]
    public int Id { get; set; }

    public DateTime LastUpdate { get; set; }
    public string LastUpdateBy { get; set; }
}
public class Location : BaseEntity
{
    [Required]
    public string Name { get; set; }

    public string Description { get; set; }
}

使用上面的示例,我想将“Id”字段映射到“LocationId”列。我知道我可以使用modelBuilder通过执行以下操作明确地为每个实体执行此操作:

modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");

但我想为我的域模型中的每个实体执行此操作,这将是丑陋的。

我尝试了以下一点反思,但没有任何运气。无论出于何种原因,编译器“无法解析符号类型”:

foreach (var type in GetTypesInNamespace(Assembly.Load("Domain.Model"),"Domain.Model"))
{
    modelBuilder.Entity<type>().Property(x=>x.Id).....
}

有没有办法定义一个约定来覆盖默认的PrimaryKey约定,将我的“Id”属性映射到数据库中的“ClassNameId”属性?我正在使用Entity Framework 6。

5 个答案:

答案 0 :(得分:2)

你应该看看Custom Code First Conventions。你需要EF6才能工作,但看起来你已经在使用了它 为了给您一个概述,请看一下我用来将PascalCase名称转换为下划线名称的以下约定。它包括id属性的约定......它还包括一个可选的表名前缀。

public class UnderscoreNamingConvention : IConfigurationConvention<PropertyInfo, PrimitivePropertyConfiguration>,
                                          IConfigurationConvention<Type, ModelConfiguration>
{
    public UnderscoreNamingConvention()
    {
        IdFieldName = "Id";
    }

    public string TableNamePrefix { get; set; }

    public string IdFieldName { get; set; }

    public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
    {
        var columnName = propertyInfo.Name;

        if (propertyInfo.Name == IdFieldName)
            columnName = propertyInfo.ReflectedType.Name + IdFieldName;

        configuration().ColumnName = ToUnderscore(columnName);
    }

    public void Apply(Type type, Func<ModelConfiguration> configuration)
    {
        var entityTypeConfiguration = configuration().Entity(type);
        if (entityTypeConfiguration.IsTableNameConfigured) return;

        var tableName = ToUnderscore(type.Name);

        if (!string.IsNullOrEmpty(TableNamePrefix))
        {
            tableName = string.Format("{0}_{1}", TableNamePrefix, tableName);
        }

        entityTypeConfiguration.ToTable(tableName);
    }

    public static string ToUnderscore(string value)
    {
        return Regex.Replace(value, "(\\B[A-Z])", "_$1").ToLowerInvariant();
    }
}

你像这样使用它

modelBuilder.Conventions.Add(new UnderscoreNamingConvention { TableNamePrefix = "app" });

编辑:在您的情况下,Apply方法应该是这样的:

public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
    if (propertyInfo.Name == "Id")
    {
        configuration().ColumnName = propertyInfo.ReflectedType.Name + "Id";
    }
}

答案 1 :(得分:1)

在DbContext类中试试这个;

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Properties<int>()
      .Where(p => p.Name.Equals("Id"))
      .Configure(c => c.HasColumnName(c.ClrPropertyInfo.ReflectedType.Name + "Id"));
}

int是我的主键字段的CLR类型。我想将代码中的所有密钥称为Id,但DBA要求密钥为带有表实体名称前缀的Id。上面给出了我在创建的数据库中的确切内容。

实体框架6.x是必需的。

答案 2 :(得分:0)

如果不使用自定义约定

,则启动动态方法
 modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");

您可以使用上下文中的反射来执行此操作。伪代码作为解释:

Reflect Context to get a list of POCO names
For each POCO in a dbcontext.
Map Property Id -> string PocoName+Id

以下是我用于此类解决方案的扩展程序。

    // DBSet Types is the Generic Types POCO name  used for a DBSet
    public static List<string> GetModelTypes(this DbContext context) {
        var propList = context.GetType().GetProperties();
        return GetDbSetTypes(propList);
    }

    // DBSet Types POCO types as IEnumerable List
    public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
        return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
                                                      .Name.StartsWith("DbSet"))
                         .Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
    }


   private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
        var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
                                     .Select(p => p.PropertyType.GenericTypeArguments[0].Name)
                                     .ToList();
        return modelTypeNames;
    }

    private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
        var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
                                 .Select(p => p.Name)
                                 .ToList();

        return modelNames;
    }

但是,您仍需要员工动态lambda才能完成。 在此处继续该主题:Dynamic lambda example with EF scenario

修改: 添加链接到另一个解决常见BAse Config类方法的问题 Abstract domain model base class when using EntityTypeConfiguration<T>

答案 3 :(得分:0)

在@ Monty0018的回答中捎带,但如果像我一样使用Entity Framework 7和/或SQLite,则需要稍微更新一下。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
        try
        {
            _builder = modelBuilder;
            var typeName = typeof(T).Name;

            _builder
                .Entity(typeof(T))
                .Property<int>("Id")
                .ForSqliteHasColumnName(typeName + "Id");
        }

        catch (Exception e)
        {
            throw e;
        }
}

答案 4 :(得分:0)

在Entity Framework 6代码优先中:

modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");

和更新数据库...

型号更改

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id { get; set; }

收件人:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long role_id { get; set; }

然后将其删除:

//modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");