【问题标题】:Get Name and Value of a property of a POCO object using an expression使用表达式获取 POCO 对象的属性的名称和值
【发布时间】:2015-03-03 07:44:29
【问题描述】:

我正在寻找一种方法来获取 POCO 对象中属性的名称和值。我尝试了很多解决方案,但似乎无法让它们发挥作用。我真的很喜欢 this 较旧的解决方案,但它会导致 null ref 错误。

这是我正在尝试做的事情:

public class POCO
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public string Description { get; set; }
    }

public class POCOValidationResult
{
    public bool IsValid { get; set; }
    public string Error { get; set; }
}

public abstract class Validator<T> where T : class
{
    public T Entity { get; set; }

    public abstract POCOValidationResult Validate();

    protected POCOValidationResult ValidateStringPropertyToLengthOf(Expression<Func<T, object>> expression, int maxLength)
    {
        var propertyName = getPropertyName(expression);
        var propertyValue = getPropertyValue(expression);

        if (propertyValue.Length > maxLength)
        {
            return new POCOValidationResult()
            {
                Error = string.Format("{0} value is too long. Must be less or equal to {1}", propertyName, maxLength.ToString())
            };
        }

        return new POCOValidationResult() { IsValid = true };
    }

    internal string getPropertyName(Expression<Func<T, object>> expression)
    {
        var memberExpersion = (MemberExpression)expression.Body;

        return memberExpersion.Member.Name;
    }
    internal string getPropertyValue<R>(Expression<Func<T, R>> expression)
    {
        //struggling to get this to work

        var me = (MemberExpression)expression.Body; // (MemberExpression)((MemberExpression)expression.Body).Expression;
        var ce = (ConstantExpression)me.Expression; // Error here!
        var fieldInfo = ce.Value.GetType().GetField(me.Member.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
        var value = fieldInfo.GetValue(ce.Value);
    }
}

public class POCOValidator : Validator<POCO>
{
    public override POCOValidationResult Validate()
    {
        var surnameValidationResult = ValidateStringPropertyToLengthOf(p => p.Surname, 10);

        if (!surnameValidationResult.IsValid)
            return surnameValidationResult;

        //var descriptionValidationResult = ValidateStringPropertyToLengthOf(p => p.Description, 100);

        //if (!descriptionValidationResult.IsValid)
        //    return descriptionValidationResult;

        //var nameValidationResult = ValidateStringPropertyToLengthOf(p => p.Name, 15);

        //if (!nameValidationResult.IsValid)
        //    return nameValidationResult;

        return new POCOValidationResult() { IsValid = true };
    }
}

public class WorkerBee
{
    public void ImDoingWorkReally()
    {
        var pocoVallidation = new POCOValidator()
        {
            Entity = new POCO()
            { 
                ID = 1, 
                Name = "James", 
                Surname = "Dean", 
                Description = "I'm not 007!"
            }
        };

        var vallidationResult = pocoVallidation.Validate();

        if (!vallidationResult.IsValid)
        {
            return;
        }

        //continue to do work...
    }
}

class Program
{
    static void Main(string[] args)
    {
        var workerBee = new WorkerBee();

        workerBee.ImDoingWorkReally();
    }
}

如您所见,我正在尝试获取属性的名称和值[通过使用表达式(p =&gt; p.Surname) 作为方法ValidateStringPropertyToLengthOf(...) 中的参数]。问题是当它调用var ce = (ConstantExpression)me.Expression; 时,我在getPropertyValue(Expression&lt;Func&lt;T, object&gt;&gt; expression) 中遇到空引用错误

那么有没有人知道如何让它发挥作用?

感谢您抽出宝贵时间对此进行调查。我真的很感激它,并希望我的问题对其他人也有帮助,因为我认为如果我能让它发挥作用,这将非常有用。

编辑:我在 cmets 中进行了如下所述的更改,但仍然收到错误“无法将 'System.Linq.Expressions.TypedParameterExpression' 类型的对象转换为 'System. Linq.Expressions.ConstantExpression”当我运行我的单元测试时。

【问题讨论】:

  • 我很确定这将解释问题stackoverflow.com/questions/3567857/…如果是这种情况,请删除您的问题。
  • 这在我看来确实是重复的。根据 leppie 的建议,您应该将方法声明更改为 string getPropertyValue&lt;TResult&gt;(Expression&lt;Func&lt;T, TResult&gt;&gt; expression) 以便传递正确的表达式类型(即带有主体的表达式类型)。我会花 5 分钟自己测试一下,但你懒得包含 a minimal, complete code example
  • 感谢您的帮助,但更改声明并没有帮助,因为我仍然收到相同的错误:“无法将 'System.Linq.Expressions.TypedParameterExpression' 类型的对象转换为 'System. Linq.Expressions.ConstantExpression'。”

标签: c# lambda expression poco


【解决方案1】:

我想出了一个解决方案(不幸的是 cmets 没有帮助)。这是有效的代码:

public class POCO
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Description { get; set; }
}

public class POCOValidationResult
{
    public bool IsValid { get; set; }
    public string Error { get; set; }
}

public abstract class Validator<T> where T : class
{
    public T Entity { get; set; }

    public abstract POCOValidationResult Validate();

    protected POCOValidationResult ValidateStringPropertyToLengthOf(Expression<Func<T, object>> expression, int maxLength)
    {
        var propertyName = getPropertyName(expression);
        var propertyValue = getPropertyValue(expression);

        if (propertyValue.Length > maxLength)
        {
            return new POCOValidationResult()
            {
                Error = string.Format("{0} value is too long. Must be less or equal to {1}", propertyName, maxLength.ToString())
            };
        }

        return new POCOValidationResult() { IsValid = true };
    }

    internal string getPropertyName(Expression<Func<T, object>> expression)
    {
        var memberExpersion = (MemberExpression)expression.Body;

        return memberExpersion.Member.Name;
    }
    internal string getPropertyValue(Expression<Func<T, object>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        var propertyInfo = memberExpression.Member as PropertyInfo;

        return propertyInfo.GetValue(Entity, null).ToString();
    }
}

public class POCOValidator : Validator<POCO>
{
    public override POCOValidationResult Validate()
    {
        var surnameValidationResult = ValidateStringPropertyToLengthOf(p => p.Surname, 10);

        if (!surnameValidationResult.IsValid)
            return surnameValidationResult;

        var descriptionValidationResult = ValidateStringPropertyToLengthOf(p => p.Description, 100);

        if (!descriptionValidationResult.IsValid)
            return descriptionValidationResult;

        var nameValidationResult = ValidateStringPropertyToLengthOf(p => p.Name, 15);

        if (!nameValidationResult.IsValid)
            return nameValidationResult;

        return new POCOValidationResult() { IsValid = true };
    }
}

public class WorkerBee
{
    public void ImDoingWorkReally()
    {
        var pocoVallidation = new POCOValidator()
        {
            Entity = new POCO()
            {
                ID = 1,
                Name = "James",
                Surname = "Dean",
                Description = "I'm not 007!"
            }
        };

        var vallidationResult = pocoVallidation.Validate();

        if (!vallidationResult.IsValid)
        {
            return;
        }

        //continue to do work...
    }
}

class Program
{
    static void Main(string[] args)
    {
        var workerBee = new WorkerBee();

        workerBee.ImDoingWorkReally();
    }
}

注意getPropertyValue的变化:

    internal string getPropertyValue(Expression<Func<T, object>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        var propertyInfo = memberExpression.Member as PropertyInfo;

        return propertyInfo.GetValue(Entity, null).ToString();
    }

【讨论】:

    【解决方案2】:

    我注意到 MaudDib 的 getPropertyValue 方法仅适用于直接在对象上的属性。例如它适用于 myObj.Value,但不适用于 myObj.Something.Value。以下适用于两者。 (不过我不知道是否有更好的方法)。

    用法:var myValue = GetPropertyValue(myObj, o =&gt; o.Something.Value);

    public static object GetPropertyValue<T>(T obj, Expression<Func<T, object>> expression)
    {
        var members = new CompositeExpressionVisitor().GetMembers(expression);
        object currentVal = obj;
        foreach (var part in members)
        {
            currentVal = GetPropertyValue(currentVal, part);
        }
    
        return currentVal;
    }
    
    private static object GetPropertyValue(object obj, MemberInfo member)
    {
        var propertyInfo = (PropertyInfo)member;
        return propertyInfo.GetValue(obj, null);
    }
    
    private class CompositeExpressionVisitor : ExpressionVisitor
    {
        private readonly List<MemberInfo> _members = new List<MemberInfo>();
    
        protected override Expression VisitMember(MemberExpression node)
        {
            _members.Add(node.Member);
            return base.VisitMember(node);
        }
    
        public IReadOnlyCollection<MemberInfo> GetMembers(Expression e)
        {
            Visit(e is LambdaExpression expression ? expression.Body : e);
            _members.Reverse();
            return _members;
        }
    }
    

    而且,如果您想要表达式的完整路径,例如你不只是想要叶子......你可以这样做:

    用法:var myValue = NameOf(() =&gt; myObj.Something.Value);

    返回:myObj.Something.Value

    或者:var myValue = NameOf(() =&gt; myObj.Something.Value, 1);

    返回:Something.Value

    public static string NameOf(Expression<Func<object>> expression, int startIndex = 0)
    {
        return new CompositeExpressionVisitor().GetPath(expression, startIndex);
    }
    
    private class CompositeExpressionVisitor : ExpressionVisitor
    {
        private readonly List<string> _parts = new List<string>();
    
        protected override Expression VisitMember(MemberExpression node)
        {
            _parts.Add(node.Member.Name);
            return base.VisitMember(node);
        }
    
        protected override Expression VisitParameter(ParameterExpression node)
        {
            _parts.Add(node.Name);
            return base.VisitParameter(node);
        }
    
        public string GetPath(Expression e, int startIndex = 0)
        {
            Visit(e is LambdaExpression expression ? expression.Body : e);
            _parts.Reverse();
            return string.Join(".", _parts.Skip(startIndex));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-21
      • 2021-01-17
      • 1970-01-01
      • 2021-10-08
      • 1970-01-01
      • 2011-02-20
      相关资源
      最近更新 更多