问题
Hello,
I am using LINQ and EF with C# 4.0. I have dragged the basic ELMAH table into EF (built and saved many many times). All is working as one would expect.
But have tried to be too ambitious and need a little help - I am trying to get the Column name from an expression that is passed in as a variable.
What I want is this:
Pass in : x=>x.ErrorId
and get : "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.
}
Thank you for your help! Dan.
回答1:
If you need to also decompose simple (or nearly simple) expressions, you'll need some extra legwork to handle the different situations. Here is some starter code that handles some common cases:
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;
}
Which produces output like:
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"
来源:https://stackoverflow.com/questions/6269822/linq-member-expression-getting-column-name