循环实体列映射以转换列名称

时间:2015-09-24 19:16:46

标签: .net entity-framework entity-framework-5 ef-fluent-api

我想在Entity Framework 5中的大量列上应用单个转换,而不必明确地将它们全部输出。作为一个例子,我想在50多列上进行以下操作(将PascalCase转换为UNDERSCORE_CASE)。

modelBuilder.Entity<Department>() 
            .Property(t => t.DepartmentName) 
            .HasColumnName("DEPARTMENT_NAME");

我找到了可以提供此功能的Dapper.FluentMap,但在创建查询时似乎无效。

有没有办法循环遍历属性列表并在模式后面指定列名?作为参考,Dapper Transform列为

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: "$1$3_$2$4")));
}

修改 这类似于this question,但它对我不起作用。也许这对EF5有不同的要求。

使用@Hopeless的答案我尝试了以下修改,但语法不太正确。我是EF的新手,所以我不熟悉如何将旧语法转换为新语法。

modelBuilder.Entity<Job>()
            .Map(m =>
{
    m.Properties<Job>(e => e.HasColumnName(name => RegEx.Replace(name, "(?<=.)(?=[A-Z])", "_").ToUpper()));
});

2 个答案:

答案 0 :(得分:1)

您可以使用Properties的{​​{1}}方法。将pascal案例模式轻松转换为下划线模式:

DbModelBuilder

模式也可以像modelBuilder.Properties() .Configure(e => e.HasColumnName(Regex.Replace(e.ClrPropertyInfo.Name, "(?<=.)(?=[A-Z])", "_").ToUpper()); 一样,替换应该是(.)([A-Z])

当然输入名称应该具有$1_$2的形式。您也可以在Dapper中找到模式(在您的问题中发布),这更适用于其他一些罕见情况(甚至包括此格式SomeThing(将转换为DDos)。是不会为你翻译成大写。

修改

遗憾的是,在EF5中,modelBuilder没有D_Dos方法。因此,对于特定的实体类型,您可以尝试:

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的属性与实体的直接属性合并。

PS :我不确定 Dapper.FluentMap 是否支持EF5,但是从您发布的代码中可以很容易如下所示附加ComplexType方法:

ToUpper()

我试过访问Dapper.FluentMap的主页,看起来它有一些基于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: "$1$3_$2$4").ToUpper())); } 的类(如果这是来自EF,则仅支持自EF6以来)。所以我不确定Dapper的代码是否适用于EF5。如果它有效,你应该尝试上面的代码以方便。

答案 1 :(得分:0)

这就是我解决它的方式。我的目标是将camelCase约定仅应用于特定的表/实体(表“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()));
}