3

我正在创建域模型,并希望有一个具有“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”属性?我正在使用实体框架 6。

4

5 回答 5

2

You should take a look at Custom Code First Conventions. You need EF6 for it to work, but it looks like you're already using it.
Just to give you an overview, take a look at the following convention I've used to convert PascalCase names to underscore names. It includes a convention for id properties... It also includes an optional table name prefix.

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

You use it like this

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

EDIT: In your case, the Apply method should be something like this:

public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
    if (propertyInfo.Name == "Id")
    {
        configuration().ColumnName = propertyInfo.ReflectedType.Name + "Id";
    }
}
于 2013-06-01T12:06:41.817 回答
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 是必需的。

于 2014-04-14T17:35:09.593 回答
1

在实体框架 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");
于 2018-09-04T10:21:34.560 回答
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 才能完成。在此处继续该主题:带有 EF 场景的动态 lambda 示例

编辑:添加指向另一个问题的链接,该问题解决了使用 EntityTypeConfiguration<T> 时的常见 BASE Con​​fig 类方法 抽象域模型基类

于 2013-05-07T23:44:35.027 回答
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;
        }
}
于 2016-08-22T14:41:31.250 回答