遍历实体列映射以转换列名
Loop over entity column mappings to transform column name
我想对 Entity Framework 5 中的大量列应用单个转换,而不必将它们全部显式键入。例如,我想在 50 多列中执行以下操作(将 PascalCase 转换为 UNDERSCORE_CASE)。
modelBuilder.Entity<Department>()
.Property(t => t.DepartmentName)
.HasColumnName("DEPARTMENT_NAME");
我发现 Dapper.FluentMap 可以提供此功能,但在创建查询时它似乎不起作用。
有没有办法遍历属性列表并按照模式指定列名?作为参考,Dapper 转换被列为
public PropertyTransformConvention()
{
Properties()
.Configure(c => c.Transform(s => Regex.Replace(input: s, pattern: "([A-Z])([A-Z][a-z])|([a-z0-9])([A-Z])", replacement: "_")));
}
编辑:
这类似于 this question 但它对我不起作用。也许这对 EF5 有不同的要求。
根据@Hopeless 的回答,我尝试了以下修改,但语法不太正确。我是 EF 的新手,所以不熟悉如何将旧语法转换为新语法。
modelBuilder.Entity<Job>()
.Map(m =>
{
m.Properties<Job>(e => e.HasColumnName(name => RegEx.Replace(name, "(?<=.)(?=[A-Z])", "_").ToUpper()));
});
可以使用DbModelBuilder
的Properties
方法。像这样轻松地将 Pascal 大小写模式转换为下划线模式:
modelBuilder.Properties()
.Configure(e => e.HasColumnName(Regex.Replace(e.ClrPropertyInfo.Name, "(?<=.)(?=[A-Z])", "_").ToUpper());
模式也可以像这样(.)([A-Z])
,那么替换应该是_
.
当然输入的名字应该是SomeThing
的形式。您还可以采用 Dapper 中的模式(发布在您的问题中),它更适用于其他一些罕见的情况(甚至包括这种格式 DDos
(将转换为 D_Dos
)。这里的要点它不会为您翻译成大写字母吗?
编辑:
遗憾的是,在 EF5 中,modelBuilder 没有 Properties()
方法。所以对于特定的实体类型,你可以试试这个:
//in your OnModelCreating scope
//names of navigation properties defined in Job should be passed
//in TransformAllColumns method
new CapsUnderscorePropertiesConfig<Job>(modelBuilder).TransformAllColumns();
//a helper class
public class CapsUnderscorePropertiesConfig<T> where T : class
{
EntityTypeConfiguration<T> _entityConfig;
Dictionary<Type, MethodInfo> _propertyMethods = new Dictionary<Type,MethodInfo>();
MethodInfo propertyForStruct;
MethodInfo propertyForNullableStruct;
public CapsUnderscorePropertiesConfig(DbModelBuilder modelBuilder)
{
_entityConfig = modelBuilder.Entity<T>();
}
void config(PropertyInfo pInfo)
{
var p = Expression.Parameter(typeof(T));
var expType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(typeof(T), pInfo.PropertyType));
MethodInfo mi;
_propertyMethods.TryGetValue(pInfo.PropertyType, out mi);
if (mi == null)
{
if (pInfo.PropertyType.IsValueType)
{
//find the Property method for struct type having argument matching Expression<Func<TEntityType, T?>>
//note the T? inside Func<...> (there is another overload but with T instead).
if (propertyForStruct == null)
{
foreach(var prop in _entityConfig.GetType().GetMethods().Where(m => m.Name == "Property" && m.IsGenericMethodDefinition)
.Select(e => new { genMethodDef = e, genMethod = e.MakeGenericMethod(pInfo.PropertyType) })){
//there should be just 2 generic Property<T> methods filtered inhere.
//One is for nullable struct and the other is for struct.
var secondFuncArgType = prop.genMethodDef.GetParameters()[0].ParameterType.GetGenericArguments()[0].GetGenericArguments()[1];
if (secondFuncArgType.IsGenericType && secondFuncArgType.GetGenericTypeDefinition() == typeof(Nullable<>))
propertyForNullableStruct = prop.genMethodDef;
else propertyForStruct = prop.genMethodDef;
}
}
mi = pInfo.PropertyType.IsGenericType && pInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ?
propertyForNullableStruct.MakeGenericMethod(pInfo.PropertyType) :
propertyForStruct.MakeGenericMethod(pInfo.PropertyType);
}
else //possible property type is string, byte[] or geo type
{
mi = _entityConfig.GetType().GetMethods().Single(m => m.Name == "Property" && !m.IsGenericMethodDefinition
&& m.GetParameters()[0].ParameterType == expType);
}
_propertyMethods[pInfo.PropertyType] = mi;
}
var propConfig = mi.Invoke(_entityConfig, new object[] { Expression.Lambda(Expression.Property(p, pInfo.Name), p) }) as PrimitivePropertyConfiguration;
propConfig.HasColumnName(Regex.Replace(pInfo.Name, "(?<=.)(?=[A-Z])", "_").ToUpper());
}
//at the time of configuring, the Metadataworkspace is not present
//So we cannot know which properties are navigation properties
//Those propertie can be excluded by passing their names in here
public void TransformAllColumns(params string[] excludedNavProperties)
{
foreach (var prop in typeof(T).GetProperties().Where(p => !excludedNavProperties.Contains(p.Name)))
{
config(prop);
}
}
}
注意:上面的代码只适用于直接属性,如果你有一些复杂的属性(返回一些ComplexType
),那么它将不起作用。从技术上讲,您需要从实体的属性中排除所有属性(返回 ComplexType),然后如果可能的话将 ComplexType
的属性与实体的直接属性合并,然后再遍历所有属性并配置每个属性。
PS:我不确定Dapper.FluentMap 支持 EF5,但是从您发布的代码来看,它可以像这样附加 ToUpper()
方法一样简单:
public PropertyTransformConvention()
{
Properties()
.Configure(c => c.Transform(s => Regex.Replace(input: s, pattern: "([A-Z])([A-Z][a-z])|([a-z0-9])([A-Z])", replacement: "_").ToUpper()));
}
我试过访问 Dapper.FluentMap 的主页,看起来它有一些基于 Convention
的 类(如果这是来自 EF,则仅从 EF6 开始支持).所以我不确定 Dapper 的代码是否适用于 EF5。如果有效,为方便起见,您应该尝试上面的代码。
我就是这样解决的。我的目标是将驼峰式命名约定仅应用于特定的 table/entity ( Table "Client" ).
modelBuilder.Properties().Configure(p => p.HasColumnName(GetDBName(p, p.ClrPropertyInfo.Name)));
private string GetDBName(ConventionPrimitivePropertyConfiguration p, string name)
{
var result = name;
var entityName = p.ClrPropertyInfo.ReflectedType.UnderlyingSystemType.Name;
if (entityName == "Client")
result = Helper.CamelCaseParaSnakeCaseOracle(name);
return result;
}
static public string CamelCaseParaSnakeCaseOracle(string input)
{
return Regex.Replace(input,
@"(?:\b|(?<=([A-Za-z])))([A-Z][a-z]*)",
m => string.Format(@"{0}{1}",
(m.Groups[1].Value.Length > 0) ? "_" : "", m.Groups[2].Value.ToUpper()));
}
我想对 Entity Framework 5 中的大量列应用单个转换,而不必将它们全部显式键入。例如,我想在 50 多列中执行以下操作(将 PascalCase 转换为 UNDERSCORE_CASE)。
modelBuilder.Entity<Department>()
.Property(t => t.DepartmentName)
.HasColumnName("DEPARTMENT_NAME");
我发现 Dapper.FluentMap 可以提供此功能,但在创建查询时它似乎不起作用。
有没有办法遍历属性列表并按照模式指定列名?作为参考,Dapper 转换被列为
public PropertyTransformConvention()
{
Properties()
.Configure(c => c.Transform(s => Regex.Replace(input: s, pattern: "([A-Z])([A-Z][a-z])|([a-z0-9])([A-Z])", replacement: "_")));
}
编辑: 这类似于 this question 但它对我不起作用。也许这对 EF5 有不同的要求。
根据@Hopeless 的回答,我尝试了以下修改,但语法不太正确。我是 EF 的新手,所以不熟悉如何将旧语法转换为新语法。
modelBuilder.Entity<Job>()
.Map(m =>
{
m.Properties<Job>(e => e.HasColumnName(name => RegEx.Replace(name, "(?<=.)(?=[A-Z])", "_").ToUpper()));
});
可以使用DbModelBuilder
的Properties
方法。像这样轻松地将 Pascal 大小写模式转换为下划线模式:
modelBuilder.Properties()
.Configure(e => e.HasColumnName(Regex.Replace(e.ClrPropertyInfo.Name, "(?<=.)(?=[A-Z])", "_").ToUpper());
模式也可以像这样(.)([A-Z])
,那么替换应该是_
.
当然输入的名字应该是SomeThing
的形式。您还可以采用 Dapper 中的模式(发布在您的问题中),它更适用于其他一些罕见的情况(甚至包括这种格式 DDos
(将转换为 D_Dos
)。这里的要点它不会为您翻译成大写字母吗?
编辑:
遗憾的是,在 EF5 中,modelBuilder 没有 Properties()
方法。所以对于特定的实体类型,你可以试试这个:
//in your OnModelCreating scope
//names of navigation properties defined in Job should be passed
//in TransformAllColumns method
new CapsUnderscorePropertiesConfig<Job>(modelBuilder).TransformAllColumns();
//a helper class
public class CapsUnderscorePropertiesConfig<T> where T : class
{
EntityTypeConfiguration<T> _entityConfig;
Dictionary<Type, MethodInfo> _propertyMethods = new Dictionary<Type,MethodInfo>();
MethodInfo propertyForStruct;
MethodInfo propertyForNullableStruct;
public CapsUnderscorePropertiesConfig(DbModelBuilder modelBuilder)
{
_entityConfig = modelBuilder.Entity<T>();
}
void config(PropertyInfo pInfo)
{
var p = Expression.Parameter(typeof(T));
var expType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(typeof(T), pInfo.PropertyType));
MethodInfo mi;
_propertyMethods.TryGetValue(pInfo.PropertyType, out mi);
if (mi == null)
{
if (pInfo.PropertyType.IsValueType)
{
//find the Property method for struct type having argument matching Expression<Func<TEntityType, T?>>
//note the T? inside Func<...> (there is another overload but with T instead).
if (propertyForStruct == null)
{
foreach(var prop in _entityConfig.GetType().GetMethods().Where(m => m.Name == "Property" && m.IsGenericMethodDefinition)
.Select(e => new { genMethodDef = e, genMethod = e.MakeGenericMethod(pInfo.PropertyType) })){
//there should be just 2 generic Property<T> methods filtered inhere.
//One is for nullable struct and the other is for struct.
var secondFuncArgType = prop.genMethodDef.GetParameters()[0].ParameterType.GetGenericArguments()[0].GetGenericArguments()[1];
if (secondFuncArgType.IsGenericType && secondFuncArgType.GetGenericTypeDefinition() == typeof(Nullable<>))
propertyForNullableStruct = prop.genMethodDef;
else propertyForStruct = prop.genMethodDef;
}
}
mi = pInfo.PropertyType.IsGenericType && pInfo.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ?
propertyForNullableStruct.MakeGenericMethod(pInfo.PropertyType) :
propertyForStruct.MakeGenericMethod(pInfo.PropertyType);
}
else //possible property type is string, byte[] or geo type
{
mi = _entityConfig.GetType().GetMethods().Single(m => m.Name == "Property" && !m.IsGenericMethodDefinition
&& m.GetParameters()[0].ParameterType == expType);
}
_propertyMethods[pInfo.PropertyType] = mi;
}
var propConfig = mi.Invoke(_entityConfig, new object[] { Expression.Lambda(Expression.Property(p, pInfo.Name), p) }) as PrimitivePropertyConfiguration;
propConfig.HasColumnName(Regex.Replace(pInfo.Name, "(?<=.)(?=[A-Z])", "_").ToUpper());
}
//at the time of configuring, the Metadataworkspace is not present
//So we cannot know which properties are navigation properties
//Those propertie can be excluded by passing their names in here
public void TransformAllColumns(params string[] excludedNavProperties)
{
foreach (var prop in typeof(T).GetProperties().Where(p => !excludedNavProperties.Contains(p.Name)))
{
config(prop);
}
}
}
注意:上面的代码只适用于直接属性,如果你有一些复杂的属性(返回一些ComplexType
),那么它将不起作用。从技术上讲,您需要从实体的属性中排除所有属性(返回 ComplexType),然后如果可能的话将 ComplexType
的属性与实体的直接属性合并,然后再遍历所有属性并配置每个属性。
PS:我不确定Dapper.FluentMap 支持 EF5,但是从您发布的代码来看,它可以像这样附加 ToUpper()
方法一样简单:
public PropertyTransformConvention()
{
Properties()
.Configure(c => c.Transform(s => Regex.Replace(input: s, pattern: "([A-Z])([A-Z][a-z])|([a-z0-9])([A-Z])", replacement: "_").ToUpper()));
}
我试过访问 Dapper.FluentMap 的主页,看起来它有一些基于 Convention
的 类(如果这是来自 EF,则仅从 EF6 开始支持).所以我不确定 Dapper 的代码是否适用于 EF5。如果有效,为方便起见,您应该尝试上面的代码。
我就是这样解决的。我的目标是将驼峰式命名约定仅应用于特定的 table/entity ( Table "Client" ).
modelBuilder.Properties().Configure(p => p.HasColumnName(GetDBName(p, p.ClrPropertyInfo.Name)));
private string GetDBName(ConventionPrimitivePropertyConfiguration p, string name)
{
var result = name;
var entityName = p.ClrPropertyInfo.ReflectedType.UnderlyingSystemType.Name;
if (entityName == "Client")
result = Helper.CamelCaseParaSnakeCaseOracle(name);
return result;
}
static public string CamelCaseParaSnakeCaseOracle(string input)
{
return Regex.Replace(input,
@"(?:\b|(?<=([A-Za-z])))([A-Z][a-z]*)",
m => string.Format(@"{0}{1}",
(m.Groups[1].Value.Length > 0) ? "_" : "", m.Groups[2].Value.ToUpper()));
}