循环实体列映射以转换列名 [英] Loop over entity column mappings to transform column name

查看:182
本文介绍了循环实体列映射以转换列名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在Entity Framework 5中的大量列上应用单一转换,而无需将其全部显式输入。作为一个例子,我想做以下超过50多列(将PascalCase转换为UNDERSCORE_CASE)。

  modelBuilder.Entity< Department> ;()
.Property(t => t.DepartmentName)
.HasColumnName(DEPARTMENT_NAME);我发现 Dapper.FluentMap ,可以提供此功能,但在创建查询时似乎不起作用。



是有一种方法来循环列表的属性并指定列名后面的模式?作为参考,Dapper Transform被列为

  public PropertyTransformConvention()
{
属性()
.Configure(c => c.Transform(s => Regex.Replace(input:s,pattern:([AZ])([AZ] [az])|([a-z0-9] )([AZ]),替换:$ 1 $ 3_ $ 2 $ 4)));
}

编辑:
这是类似的此问题,但无效为了我。也许这对EF5有不同的要求。



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

  modelBuilder.Entity< Job> ()
.Map(m =>
{
m.Properties< Job>(e => e.HasColumnName(name =® RegEx.Replace(name, < =。)(?= [AZ]),_)ToUpper()));
});


解决方案

您可以使用属性方法 DbModelBuilder 。将pascal案例模式轻松地转换为下划线图案:

  modelBuilder.Properties()
.Configure(e = > e.HasColumnName(Regex.Replace(e.ClrPropertyInfo.Name,(?< =。)(?= [AZ]),_)ToUpper());

模式也可以像这样(。)([AZ])然后替换应该是 $ 1_ $ 2



当然,输入名称的格式应该是 SomeThing 。您还可以在Dapper(发布在您的问题中)中使用模式,这对于其他一些罕见的情况(甚至包括此格式 DDos (将转换为 D_Dos )。这里的意思是它不会转换为大写。


$可惜,在EF5中,modelBuilder没有属性() 方法,所以对于一个特定的实体类型,你可以尝试这样:

  //在您的OnModelCreating范围中
//在Job中定义的导航属性名称应在TransformAllColumns方法中传递
//
new CapsUnderscorePropertiesConfig< Job>(modelBuilder).TransformAllColumns();

//一个助手类
public class CapsUnderscorePropertiesConfig< T>其中T:class
{
EntityTypeConfiguration< T> _entityConfig;
字典< 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)
{
//找到具有参数匹配的结构类型的Property方法Expression< ; Func< TEntityType,T?>>>
//注意T?内部Func< ...> (还有另一个重载,但用T代替)。
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)})){
//应该只有2个通用性< T>方法过滤了。
//一个是可空结构,另一个是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 //可能的属性类型是string,byte []或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)})作为PrimitivePropertyConfiguration;
propConfig.HasColumnName(Regex.Replace(pInfo.Name,(?< =。)(?= [A-Z]),_)ToUpper());
}
//在配置时,Metadataworkspace不存在
//所以我们不知道哪些属性是导航属性
//那些属性可以通过传递被排除他们的名字在这里
public void TransformAllColumns(params string [] excludedNavProperties)
{
foreach(varof in typeof(T).GetProperties()。Where(p =>!excludedNavProperties。包含(p.Name)))
{
config(prop);
}
}
}

注意: / strong>以上代码仅适用于直接属性,如果您有一些复杂的属性(返回一些 ComplexType ),那么它将无法正常工作。从技术上讲,您需要从实体的属性中排除所有属性(返回ComplexType),然后如果可能,在循环遍历和配置每个属性之前,将 ComplexType 的属性与实体的直接属性合并。



PS :我不知道如果 Dapper.FluentMap 支持EF5,但是从您发布的代码中,可以像这样添加 ToUpper()方法一样简单:

  public PropertyTransformConvention()
{
属性()
.Configure(c => c.Transform(s => Regex.Replace(input: s,pattern:([AZ])([AZ] [az])|([a-z0-9])([AZ]),替换:$ 1 $ 3_ $ 2 $ 4)ToUpper );
}

我试过访问Dapper.FluentMap的主页,看起来像有一些基于约定的课程(如果来自EF,则仅在EF6之后才支持)。所以我不知道Dapper的代码是否适用于EF5。如果它有效,您应该尝试上面的代码以方便。


I would like to apply a single transformation over a large number of columns in Entity Framework 5 without having to explicitly type them all out. As an example I would like to do the following over 50+ columns (convert PascalCase to UNDERSCORE_CASE).

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

I found the Dapper.FluentMap which can provide this functionality but it doesn't appear to work when creating the query.

Is there a way to loop over the list of properties and specify the column name following a pattern? For reference the Dapper Transform is listed as

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

EDIT: This is similar to this question but it does not work for me. Perhaps this has different requirements for EF5.

Using the answer from @Hopeless I have attempted the following modification but the syntax is not quite right. I am new to EF so am not familiar with how to convert the older syntax to the newer.

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

解决方案

You can use the Properties method of DbModelBuilder. Translate the pascal case pattern to underscore pattern easily like this:

modelBuilder.Properties()
            .Configure(e => e.HasColumnName(Regex.Replace(e.ClrPropertyInfo.Name, "(?<=.)(?=[A-Z])", "_").ToUpper());

The pattern can also be like this (.)([A-Z]) and the replacement should then be $1_$2.

Of course the input name should exactly have form of SomeThing. You can also take the pattern in Dapper (posted in your question), which works more exactly for some other rare cases (even including this format DDos (which will be converted to D_Dos). The point here is it does not translate to uppercase for you.

Edit:

It's a pity that in EF5, modelBuilder does not have Properties() method. So for a specific entity type, you can try this:

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

NOTE: The code above works only for direct properties, if you have some complex properties (returning some ComplexType), then it won't work. Technically you need to exclude all properties (returning ComplexType) from the entity's properties, then if possible merge the properties of ComplexType with the entity's direct properties before looping through all and configuring each.

PS: I'm not sure if Dapper.FluentMap supports EF5, but from the code you posted, it can be as easy as appending the ToUpper() method like this:

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

I've tried visiting the homepage of Dapper.FluentMap and looks like that it has some classes based on Convention (if this is from EF, it is supported only since EF6). So I'm not sure if the Dapper's code works in EF5. If it works you should try the code above for convenience.

这篇关于循环实体列映射以转换列名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆