3

假设我从服务(我无法控制)获取数据:

public class Data
{
    // an array of column names
    public string[] ColumnNames { get; set; }

    // an array of rows that contain arrays of strings as column values
    public string[][] Rows { get; get; }
}

在中间层,我想将其映射/翻译到可以将其中IEnumerable<Entity>的列名表示为我的类中的属性的位置。我说可能是因为我可能不需要服务返回的所有数据,而只需要其中的一部分。Data Entity

转型

这是进行翻译的算法的抽象:

  1. 创建一个IDictionary<string, int>ColumnNames以便我可以轻松地将各个列名映射到各个行中的数组索引。
  2. 使用反射来检查我的Entity属性名称,以便我能够将它们与列名匹配
  3. 根据 #1 中完成的映射遍历Data.Rows并创建我的对象并填充属性。Entity可能使用反射和SetValue属性来设置它们。

优化

上层算法当然可以工作,但我认为因为它使用反射,它应该做一些缓存,可能还有一些即时编译,这可以大大加快速度。

完成第 1 步和第 2 步后,我们实际上可以生成一个方法,该方法采用字符串数组并直接使用索引实例化我的实体,然后对其进行编译和缓存以供将来重用。

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

附加事实

这不是问题(和答案)的必要条件,但我还创建了两个属性,当它们的名称不匹配时,它们有助于列到属性的映射。我创建了最明显的MapNameAttribute(它需要一个字符串,并且还可以选择启用区分大小写)和我的不应该映射到任何数据IgnoreMappingAttribute的属性。Entity但是这些属性是在上层算法的第 2 步中读取的,因此会根据此声明性元数据收集和重命名属性名称,以便它们与列名称匹配。

问题

生成和编译这种方法的最佳和最简单的方法是什么?Lambda 表达式?CSharpCodeProvider班级?

你可能有一个生成和编译代码的例子吗?我猜映射是一个相当常见的场景。

注意:与此同时,我将研究 PetaPoco(也许还有 Massive),因为 afaik 他们都在运行中进行编译和缓存,完全是为了映射目的。

4

1 回答 1

2

建议:从 NuGet 获取 FastMember

然后只需使用:

var accessor = TypeAccessor.Create(typeof(Entity));

然后在你的循环中,当你找到当前迭代的memberNameand时:newValue

accessor[obj, memberName] = newValue;

这是为了满足您的要求而设计的;在内部,它维护了一组类型(如果以前见过)。当看到一个新类型时,它会创建一个新的TypeAccessoron-the-fly (via TypeBuilder) 子类并缓存它。每个独特TypeAccessor的都知道该类型的属性,并且基本上就像:

switch(memberName) {
    case "Foo": obj.Foo = (int)newValue;
    case "Bar": obj.Bar = (string)newValue;
    // etc
}

因为这是缓存的,所以您只需在它第一次看到您的类型时支付任何费用(而且不是真正的大笔费用);其余时间,它是免费的。因为它ILGenerator 直接使用,它也避免了任何不必要的抽象,例如 viaExpression或 CodeDom,所以它几乎是尽可能快的。

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


额外的:

可以做的是:获取现有FastMember代码,并将其编辑为 processMapNameAttributeIgnoreMappingAttributeduringWriteGetterWriteSetter; 然后所有的巫术都发生在您的数据名称上,而不是成员名称上。

这将意味着更改行:

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

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

WriteGetterand中,如果应该忽略它,则在循环的开头WriteSetter执行 a 。continueforeach

于 2012-07-26T09:32:43.507 回答