我正在创建域模型,并希望拥有一个带有“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。
答案 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");