【问题标题】:LINQ member expression getting column nameLINQ 成员表达式获取列名
【发布时间】:2011-06-08 02:56:24
【问题描述】:

您好,

我在 C# 4.0 中使用 LINQ 和 EF。 我已将基本的 ELMAH 表拖到 EF 中(多次构建和保存)。 一切正常。

但过于雄心勃勃,需要一点帮助 - 我试图从作为变量传入的表达式中获取列名。

我想要的是这个:

传入:x=>x.ErrorId

并得到:“ErrorId”

public void GetColumnName(Expression<Func<T, object>> property)
{
  // The parameter passed in x=>x.Message
  // Message works fine (probably because its a simple string) using:
  string columnName = (property.Body as MemberExpression).Member.Name;

  // But if I attempt to use the Guid or the date field then it
  // is passed in as x => Convert(x.TimeUtc)
  // As a result the above code generates a NullReference exception
  // i.e. {"Object reference not set to an instance of an object."}

  // What is the correct code here to extract the column name generically?
  // Ideally in a way that won't bite me again in the future.

}

感谢您的帮助! 丹。

【问题讨论】:

  • 所以您希望通过一个可能比x.ColumnName 更复杂的表达式来确定列名?
  • 尝试调试并将property.Body as MemberExpression 放入手表中,一旦您点击ErrorId,您可能会看到如何提取它
  • @oleksii 酷我去看看

标签: c# linq entity-framework linq-expressions


【解决方案1】:

如果您还需要分解简单(或几乎简单)的表达式,则需要一些额外的工作来处理不同的情况。下面是一些处理一些常见情况的起始代码:

string GetColumnName<T,TResult>(Expression<Func<T,TResult>> property)
{
    var member = GetMemberExpression(property.Body);
    if (member == null)
        throw new ArgumentException("Not reducible to a Member Access", 
                                    "property");

    return member.Member.Name;
}

MemberExpression GetMemberExpression(Expression body)
{
    var candidates = new Queue<Expression>();
    candidates.Enqueue(body);
    while (candidates.Count > 0)
    {
        var expr = candidates.Dequeue();
        if (expr is MemberExpression)
        {
            return ((MemberExpression)expr);
        }
        else if (expr is UnaryExpression)
        {
            candidates.Enqueue(((UnaryExpression)expr).Operand);
        }
        else if (expr is BinaryExpression)
        {
            var binary = expr as BinaryExpression;
            candidates.Enqueue(binary.Left);
            candidates.Enqueue(binary.Right);
        }
        else if (expr is MethodCallExpression)
        {
            var method = expr as MethodCallExpression;
            foreach (var argument in method.Arguments)
            {
                candidates.Enqueue(argument);
            }
        }
        else if (expr is LambdaExpression)
        {
            candidates.Enqueue(((LambdaExpression)expr).Body);
        }
    }

    return null;
}

产生如下输出:

GetColumnName((x) => x.X): "X"
GetColumnName((x) => x.X + 2): "X"
GetColumnName((x) => 2 + x.X): "X"
GetColumnName((x) => -x.X): "X"
GetColumnName((x) => Math.Sqrt(x.Y)): "Y"
GetColumnName((x) => Math.Sqrt(Math.Abs(x.Y))): "Y"

【讨论】:

  • 使用几乎完全相同的代码,尤其是在 EF 中修复这个愚蠢的 Include 语句,由于字符串作为参数传递,因此极难重构。
  • 绝妙的答案!奇迹般有效。有什么我应该注意的限制吗?你知道我在 MVC 中有一个 HTMLHelper,它采用这些 lambda 表达式来构建一个自定义的字段列表来显示(覆盖默认值),所以对于我需要的东西来说应该是超级的。谢谢!
  • @Dan:如果您有多个属性,它将返回最左边的属性。此外,它将返回 any 成员访问的最左侧。这意味着像(x) =&gt; y.PropA + x.PropB 这样一些复杂的表达式会报告PropA
  • 好的 - 很高兴知道。你让我的夜晚非常愉快!再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-23
  • 2023-04-06
相关资源
最近更新 更多