假设我从服务(我无法控制)获取数据:
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
中的列名可能表示为{{1}中的属性}类。我说可能因为我可能不需要服务返回的所有数据,只是其中一些数据。
这是一个可以进行翻译的算法的抽象:
Entity
IDictionary<string, int>
,以便我可以轻松地将各个列名映射到各行中的数组索引。ColumnNames
属性名称,以便我可以将它们与列名匹配Entity
并创建我的Data.Rows
对象,并根据#1中的映射填充属性。可能在属性上使用反射和Entity
来设置它们。上层算法当然可以工作,但我认为因为它使用反射它应该做一些缓存,可能还有一些动态编译,这可以大大加快速度。
当完成第1步和第2步时,我们实际上可以生成一个方法,该方法接受一个字符串数组并直接使用索引实例化我的实体并将其编译为并将其缓存以供将来重用。
我通常会得到一页结果,因此后续请求会重复使用相同的编译方法。
这不是问题(和答案)的必要条件,但我还创建了两个属性,当这些属性在名称中不匹配时,它们有助于列到属性的映射。我创建了最明显的SetValue
(需要一个字符串,也可以选择启用区分大小写)和MapNameAttribute
我IgnoreMappingAttribute
上不应映射到任何数据的属性。但是这些属性在上层算法的第2步中读取,因此根据此声明性元数据收集和重命名属性名称,以便它们匹配列名。
生成和编译这种方法的最佳和最简单的方法是什么? Lambda表达式? Entity
上课?
你是否有一个生成和编译代码的例子做类似的事情?我想这些映射是一种相当普遍的情况。
注意:与此同时,我将检查PetaPoco(也许还有Massive),因为afaik他们都是为了映射而动态进行编译和缓存。
答案 0 :(得分:2)
建议:obtain FastMember from NuGet
然后使用:
var accessor = TypeAccessor.Create(typeof(Entity));
然后在您的循环中,当您找到当前迭代的memberName
和newValue
时:
accessor[obj, memberName] = newValue;
这是为了满足您的要求;在内部,如果以前见过,它会维护一组类型。当看到新类型时,它会动态创建TypeAccessor
的新子类(通过TypeBuilder
)并对其进行缓存。每个唯一TypeAccessor
都知道该类型的属性,基本上就像:
switch(memberName) {
case "Foo": obj.Foo = (int)newValue;
case "Bar": obj.Bar = (string)newValue;
// etc
}
因为这是缓存的,所以您第一次看到自己的类型时,只需支付任何费用(而不是真正的大费用);其余时间,它是免费的。因为它直接使用ILGenerator
,所以它也避免了任何不必要的抽象,例如通过Expression
或CodeDom,因此速度尽可能快。
(我还应该澄清,对于dynamic
类型,即实现IDynamicMetaObjectProvider
的类型,它可以使用单个实例来支持每个对象。)
其他:
{1}};然后所有巫术都发生在您的数据名称上,而不是成员名称。
这意味着改变行:
FastMember
和
MapNameAttribute
在IgnoreMappingAttribute
和WriteGetter
中,并在WriteSetter
循环开始时执行il.Emit(OpCodes.Ldstr, prop.Name);
,如果应该忽略它。