C# 使用LINQ表达式指定给对象';s属性

C# 使用LINQ表达式指定给对象';s属性,c#,linq,reflection,C#,Linq,Reflection,所以我使用的是一个旧的数据模型,我必须在我得到的范围内工作 当我执行数据库查询时,模型将数据作为 List<Dictionary<string, object>> 然后像这样转换 List<Dictionary<string, object>> Results = MyModel.Query(...); List<ProductViewModel> POCOs = new List<ProductViewModel>();

所以我使用的是一个旧的数据模型,我必须在我得到的范围内工作

当我执行数据库查询时,模型将数据作为

List<Dictionary<string, object>>
然后像这样转换

List<Dictionary<string, object>> Results = MyModel.Query(...);
List<ProductViewModel> POCOs = new List<ProductViewModel>();

foreach (var Result in Results) // Foreach row
{
  ProductViewModel POCO = new ProductViewModel();

  foreach (var i in Result) // Foreach column in this row
  {
    // This is where I need help.
    // i.Key is the string name of my column.
    // I can get the lambda for this property from my map using this column name.
    // For example, need to assign to POCO.Id using the lambda expression p => p.Id
    // Or, assign to POCO.Code using the lambda expression p => p.Code
  }

  POCOs.Add(POCO);
}

return POCOs;
List Results=MyModel.Query(…);
List POCOs=新列表();
foreach(结果中的var结果)//foreach行
{
ProductViewModel POCO=新的ProductViewModel();
foreach(结果中的var i)//此行中的foreach列
{
//这就是我需要帮助的地方。
//键是我列的字符串名称。
//我可以使用此列名从映射中获取此属性的lambda。
//例如,需要使用lambda表达式p=>p.Id分配给POCO.Id
//或者,使用lambda表达式p=>p.Code分配给POCO.Code
}
POCO.Add(POCO);
}
返回POCO;

这可以使用某种反射来实现吗?如果可以,如何实现?

您可以做的是使用类似这样的方法从某种类型的linq表达式中提取属性名
p=>p.Id

public static string GetPropertyName<T>(Expression<Func<T>> expression)
{
    MemberExpression body = (MemberExpression)expression.Body;
    return body.Member.Name;
}
公共静态字符串GetPropertyName(表达式)
{
MemberExpression body=(MemberExpression)expression.body;
返回body.Member.Name;
}
..然后使用普通的旧反射将值实际分配给对象实例。例如,创建一个方法

private void Assign(object objInstance, Expression<Func<T>> propertyExpression, object value)
{
    string propertyNameToAssign = GetPropertyName(propertyExpression);

    //TODO use reflection to assign "value" to the property "propertyNameToAssign" of "objInstance"
}
private void Assign(对象对象对象状态、表达式属性表达式、对象值)
{
字符串propertyNameToAssign=GetPropertyName(propertyExpression);
//TODO使用反射为“对象”的属性“propertyNameToAssign”指定“值”
}

(没有编译代码;对于反射部分,web上有很多文章。希望这能有所帮助)

这似乎是
动态
的完美匹配。您可以创建一个动态类,该类的属性基于字典中的键。检查
DynamicObject
类。

下面是一个使用的方法。首先,定义映射的API:

public class PropertyMap<T> where T : new()
{
    public void Add(string sourceName, Expression<Func<T, object>> getProperty);

    public T CreateObject(IDictionary<string, object> values);
}
然后,您将使用表达式树动态编译一个方法,该方法执行赋值(听起来比实际情况更可怕)。可视化此过程的最简单方法是查看我们正在构建的示例。我们想要的是一些代码,它可以做到这一点:

new ProductViewModel
{
    Id = ...,
    Code = ...,
    Description = ...,
    Active = ...
}
但是,由于动态映射,我们在编译时无法知道这一点。因此,我们将构建一个函数,该函数就是精确的代码,但在运行时编译。表达式树只是运行时数据,表示可以在编译时编写的相同代码

首先,我们需要为属性获取一组绑定(赋值):

private readonly IDictionary<string, PropertyInfo> _properties = new Dictionary<string, PropertyInfo>();
private IEnumerable<MemberBinding> GetPropertyBindings(IDictionary<string, object> values)
{
    return
        from sourceName in _properties.Keys
        select Expression.Bind(_properties[sourceName], Expression.Constant(values[sourceName]));
}
因为我们在类声明中指定了
where T:new()
,所以我们保证在这里调用一个无参数构造函数。我们传入之前创建的属性绑定,为我们提供了一个数据结构,表示我们想要构建的初始化表达式

那么我们知道什么呢?我们有这个数据结构,但是我们如何调用代码呢?要做到这一点,我们必须将该表达式包装到一个我们可以调用的函数中,因为实际上唯一可以调用的是一个方法。这意味着我们正在构建如下代码:

var map = new PropertyMap<ProductViewModel>();

map.Add("Id", p => p.Id);
map.Add("Code", p => p.Code);
map.Add("Description", p => p.Description);
map.Add("Active", p => p.Active);

var productViewModel = map.CreateObject(values);
() => new ProductViewModel
{
    Id = ...,
    Code = ...,
    Description = ...,
    Active = ...
}
这是一个无参数函数,调用时将返回初始化的对象。这也称为lambda表达式。我们可以得到这样的数据结构:

private Func<T> GetInitializationFunction(IDictionary<string, object> values)
{
    var initializationLambda = Expression.Lambda<Func<T>>(GetMemberInit(values));

    return initializationLambda.Compile();
}

似乎C#4.0添加了一种称为表达式树的东西,它们对这一点有用吗?如果是的话,它们的性能与您展示的反射相比如何?我实际上没有对表达式树进行太多的处理,但据我所知,它们只是对某些数据施加结构的一种方式,基本上是通过在这些所谓的表达式树中编码信息。实际上,
GetPropertyName
已经对这种表达式进行了一些非常基本的使用。然而,最终还是需要反射来提取实际值(据我所知)。我目前正在研究AutoMapper是否可以在这里使用,但我有我的疑问。这不是一个流行词,而是一个关键词。哇,非常感谢!与直接使用反射相比,这将如何执行?我猜,一旦表达式被编译,它的速度就和本机代码一样快,对吗?@Baconcheese:没错-一旦你有了
Func
,就好像你传递了一个对你自己编写的方法的引用一样。这里也有大量的缓存机会;例如,您可能仅在第一次和添加映射后生成该方法。然后,如果在应用程序级别缓存
PropertyMap
实例,则开销很小。
private MemberInitExpression GetMemberInit(IDictionary<string, object> values)
{
    return Expression.MemberInit(Expression.New(typeof(T)), GetPropertyBindings(values));
}
() => new ProductViewModel
{
    Id = ...,
    Code = ...,
    Description = ...,
    Active = ...
}
private Func<T> GetInitializationFunction(IDictionary<string, object> values)
{
    var initializationLambda = Expression.Lambda<Func<T>>(GetMemberInit(values));

    return initializationLambda.Compile();
}
public T CreateObject(IDictionary<string, object> values)
{
    var initializationFunction = GetInitializationFunction(values);

    return initializationFunction();
}