Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/315.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 生成并编译名称到索引的转换/映射,以实现更快的重用性_C#_Compilation_Generated Code - Fatal编程技术网

C# 生成并编译名称到索引的转换/映射,以实现更快的重用性

C# 生成并编译名称到索引的转换/映射,以实现更快的重用性,c#,compilation,generated-code,C#,Compilation,Generated Code,假设我从服务(我无法控制)获取数据,如下所示: 在中间层,我想将其映射/转换为一个IEnumerable,其中数据中的列名可以作为我的实体类中的属性来表示。我说可能是因为我可能不需要服务返回的所有数据,只需要其中的一部分 转型 这是进行转换的算法的抽象: 创建一个ColumnNames的IDictionary,这样我就可以轻松地将各个列名映射到各个行中的数组索引 使用反射检查我的实体属性的名称,以便能够将它们与列名匹配 遍历数据.行,创建我的实体对象,并根据#1中完成的映射填充属性。可能在属性上

假设我从服务(我无法控制)获取数据,如下所示:

在中间层,我想将其映射/转换为一个
IEnumerable
,其中
数据中的列名可以作为我的
实体
类中的属性来表示。我说可能是因为我可能不需要服务返回的所有数据,只需要其中的一部分

转型 这是进行转换的算法的抽象:

  • 创建一个
    ColumnNames
    IDictionary
    ,这样我就可以轻松地将各个列名映射到各个行中的数组索引
  • 使用反射检查我的
    实体
    属性的名称,以便能够将它们与列名匹配
  • 遍历
    数据.行
    ,创建我的
    实体
    对象,并根据#1中完成的映射填充属性。可能在属性上使用反射和设置值来设置它们
  • 优化 上面的算法当然可以工作,但我认为,因为它使用反射,所以它应该进行一些缓存和可能的动态编译,这可以大大加快速度

    当第1步和第2步完成时,我们实际上可以生成一个方法,该方法接受一个字符串数组,并直接使用索引实例化实体,然后编译它并缓存它,以备将来重用

    我通常会得到一页结果,因此后续请求将重用相同的编译方法

    附加事实 这对于问题(和答案)不是必须的,但我还创建了两个属性,当这些属性的名称不匹配时,它们有助于列到属性的映射。我为我的
    实体
    上不应映射到任何数据的属性创建了最明显的
    MapNameAttribute
    (该属性采用字符串,并可选地启用区分大小写)和
    IgnoreMappingAttribute
    。但这些属性是在上面算法的第2步中读取的,所以属性名是根据声明性元数据收集和重命名的,以便它们与列名匹配

    问题: 生成和编译这样一个方法的最佳和最简单的方法是什么?Lambda表达式<代码>CSharpCodeProvider

    您是否有一个生成和编译代码的示例,可以执行类似的操作?我猜映射是一种相当常见的场景

    注意:在此期间,我将研究PetaPoco(可能也是大规模的),因为它们都是为了映射目的而动态编译和缓存的

    建议:

    然后使用:

    var accessor = TypeAccessor.Create(typeof(Entity));
    
    然后在循环中,当您找到当前迭代的
    memberName
    newValue
    时:

    accessor[obj, memberName] = newValue;
    
    这是为了满足你的要求而设计的;在内部,它维护一组类型(如果以前见过)。当看到一个新类型时,它会动态地(通过
    TypeBuilder
    )创建
    TypeAccessor
    的新子类并缓存它。每个唯一的
    TypeAccessor
    都知道该类型的属性,基本上就像:

    switch(memberName) {
        case "Foo": obj.Foo = (int)newValue;
        case "Bar": obj.Bar = (string)newValue;
        // etc
    }
    
    因为这是缓存的,所以在它第一次看到您的类型时,您只需支付任何费用(实际上不是很大的费用);其余时间都是免费的。因为它直接使用
    ILGenerator
    ,所以它还避免了任何不必要的抽象,例如通过
    Expression
    或CodeDom进行抽象,所以它的速度尽可能快

    (我还应该澄清,对于
    动态
    类型,即实现
    IDynamicMetaObjectProvider
    的类型,它可以使用单个实例来支持每个对象)


    其他:

    您可以做的是:获取现有的
    FastMember
    代码,并在
    WriteGetter
    WriteSetter
    期间编辑它以处理
    MapNameAttribute
    IgnoreMappingAttribute
    ;然后所有的巫毒都发生在你的数据名上,而不是成员名上

    这将意味着改变路线:

    il.Emit(OpCodes.Ldstr, prop.Name);
    


    WriteGetter
    WriteSetter
    中,如果应该忽略,则在
    foreach
    循环的开头执行
    continue

    在这里有用吗?或者只是看看它是如何工作的?(所有代码都可用)@MarcGravel:是的,马克,这正是我想要的。基本上包装很好。:)你能把这一点写进答案中,并提供一些关于缓存的附加信息吗?+1在你的迷你库中完成并被接受,因为它太好了。:)谢谢你,马克。我还可以做一些其他的改变,比如删除直接转换,而是使用
    Convert
    type.TryParse
    方法,因为我的数据总是用字符串表示。。。当您知道类型匹配时,建议在其他情况下使用转换。。。
    il.Emit(OpCodes.Ldstr, prop.Name);
    
    il.Emit(OpCodes.Ldstr, field.Name);