【问题标题】:Auto Column Mapping Using Reflection and expression使用反射和表达式的自动列映射
【发布时间】:2018-07-19 11:40:47
【问题描述】:

是否可以通过反射自动执行这些映射?

我有一个简单的列映射:

var columnsMap = new Dictionary<string, Expression<Func<Industry, object>>>
{
    ["id"] = v => v.Id,
    ["name"] = v => v.Name,
    ["isActive"] = v => v.IsActive
};

columnsMap 运行时结果:


我想在包装类中自动化这些映射:

 public class QueryColumnMapper<T> : Dictionary<string, Expression<Func<T, object>>>
    {
        public QueryColumnMapper<T> GenerateMappings()
        {
            foreach (var item in typeof(T).GetProperties())
            {
                // get dictionary key ======> its OK
                var name = Char.ToLowerInvariant(item.Name[0]) + item.Name.Substring(1); //camel-case name

                // get expression    =======> this is the problem. wrong way
                Expression<Func<T, object>> exp = v => v.GetType().GetProperty(item.Name);
                //Expression<Func<T, object>> exp = v => v.?????;    <-----   

                // add to mapper object
                this.Add(name, exp);
            }
            return this;
        }
    }

使用这个类的例子:

var columnsMap = new QueryColumnMapper<Industry>().GenerateMappings();  

columnsMap 运行时结果:

我不知道是否可以在运行时动态获取我的表达式?

(我正在使用此映射字典在实体框架IQueryable 查询上应用过滤。第一个示例(手动映射)它可以工作,但我不知道如何在没有手动映射的情况下在运行时执行此操作)

【问题讨论】:

  • this is the problem 是什么错误?
  • 您希望它与 Enttiy 框架一起使用吗?请edit你的问题并解释你到底想要完成什么,因为这听起来像XY problem
  • 我编辑了这篇文章。谢谢
  • @thehennyy 问题是我做错了方式......它不起作用,因为我不想在 columnsMap 中获得第二个结果..
  • @CamiloTerevinto 我想要像我的手动映射字典那样的对象映射,而不是属性值

标签: c# entity-framework reflection .net-core expression-trees


【解决方案1】:

您可以使用属性名称手动构建自定义表达式

// x =>
var parameter = Expression.Parameter(typeof(T));
// x.Name
var mapProperty = Expression.Property(parameter, "Name");
// (object)x.Name
var convertedExpression = Expression.Convert(mapProperty, typeof(object));
// x => (object)x.Name
var exp = Expression.Lambda<Func<T, object>>(convertedExpression, parameter);

如果这是一个公共子集,并且您发现您经常这样做,您可以创建一个基本接口。

【讨论】:

  • 答案的第一部分有什么用处呢?无论界面如何,OP 都不想手动进行映射。这个答案可以用“替代”来解决
  • @CamiloTerevinto 我认为值得一提,但我想我可以删除它
  • @CamiloTerevinto,我可以在此处对对象进行强制转换还是需要添加转换语句?
  • 编译时没有强制转换,它是在运行时评估表达式时确定的。我将该代码用于Where 动态调用,它工作得很好
  • @Alireza 我在每一步都添加了 cmets 以了解正在发生的事情
【解决方案2】:

对于其他观众,我分享了最终代码:

 public class QueryColumnMapper<T> 
{
    public QueryColumnMapper()
    {
        Mappings = new Dictionary<string, Expression<Func<T, object>>>();
    }
    public Dictionary<string, Expression<Func<T, object>>> Mappings { get; set; }
    public Dictionary<string, Expression<Func<T, object>>> GenerateMappings()
    {
        foreach (var item in typeof(T).GetProperties())
        {
            var name = Char.ToLowerInvariant(item.Name[0]) + item.Name.Substring(1); //camel-case name

            // add to mapper object
            Mappings.Add(name, GetExpression(item.Name));
        }
        return Mappings;
    }

    private Expression<Func<T,object>> GetExpression(string propertyName)
    {
        // x =>
        var parameter = Expression.Parameter(typeof(T));
        // x.Name
        var mapProperty = Expression.Property(parameter, propertyName);
        // (object)x.Name
        var convertedExpression = Expression.Convert(mapProperty, typeof(object));
        // x => (object)x.Name
        return Expression.Lambda<Func<T, object>>(convertedExpression, parameter);
    }
}

用法:

var columnsMap = new QueryColumnMapper<Industry>().GenerateMappings();

【讨论】:

  • 我很高兴你能用 +1 来显示完整的代码,但这只是一个建议,而不是从字典继承,你可能只需要一个包含字典的类
  • 请问这是什么原因?我继承了字典,因为我在服务的其他部分使用该类。 (类似于 ApplyFiltering 方法)
  • 一般来说从Collections继承是不好的,除非你需要扩展collection的功能,这个post会解释原因
  • @johnny5 好的,我根据您的建议更新了最终代码。但现在我想知道为什么我在这里使用一个类:) 我只需要那个功能。!通过这些更改,我根本没有使用这个类。我应该在 GenerateMappings 方法中返回类实例吗?还是正确的方式?
  • 您可以直接将地图发回,但最终您可能会发现除了地图之外还需要其他元数据,因此最好将其留在课堂上,但这取决于您,例如您可能想要添加方便的方法来添加表达式,或者您可能想要隐藏地图的实现并关闭应用函数。
猜你喜欢
  • 2013-11-13
  • 1970-01-01
  • 2021-10-28
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多