LINQ成员表达式获取列名

6

你好,

我正在使用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ж›өе¤Қжқ‚зљ„иҰЁиңңеәЏдё­зҰ®е®ље€—еђҚеђ—пәџ - user7116
尝试调试并将 property.Body as MemberExpression 放入监视窗口,一旦触发 ErrorId,您可能会看到如何提取它。 - oleksii
1个回答

6
如果你需要解析简单(或几乎简单)的表达式,你需要进行额外的努力来处理不同的情况。以下是一些起始代码,可以处理一些常见情况:
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语句,因为传递的参数是字符串,所以极难重构。 - vittore
非常棒的答案!运行得很好。我需要注意哪些限制吗?只是让你知道,在MVC中我有一个HTMLHelper,它使用这些lambda表达式来构建自定义字段列表以显示(覆盖默认值),所以对我来说应该非常好用。谢谢! - Dan B
@Dan:如果你在其中有多个属性,它将返回最左边的属性。此外,它将返回任何成员访问的最左边。这意味着一些复杂的表达式,如(x) => y.PropA + x.PropB将报告PropA - user7116
好的 - 很高兴知道。你让我的晚上非常愉快!再次感谢! - Dan B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接