Get Name and Value of a property of a POCO object

2019-08-10 21:11发布

I'm looking for a way to get the name and value of a proptery in a POCO object. I've tried many solutions but can't seem to get them to work. I really liked this older solution but it causes a null ref error.

Here's kind of what I'm trying to do:

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();
    }
}

So as you can see, I'm trying to get the Property's name and value [by using an Expression (p => p.Surname) as a parameter in the method ValidateStringPropertyToLengthOf(...)]. The problem is that I'm getting a null ref error in getPropertyValue(Expression<Func<T, object>> expression) when it calls var ce = (ConstantExpression)me.Expression;

So does anyone have ideas on how to get this to work?

Thanks for taking the time to look into this. I really appreciate it and hope that my question also is helpful for others as I think this can be rather useful if I can get this to work.

EDIT: I've made the change as mentioned below in the comments and still getting the error "Unable to cast object of type 'System.Linq.Expressions.TypedParameterExpression' to type 'System.Linq.Expressions.ConstantExpression" when I run my unit test.

1条回答
太酷不给撩
2楼-- · 2019-08-10 21:22

I worked out a solution (unfortunately the comments were not helpful). Here's the code that works:

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();
    }
}

Note the change for 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();
    }
查看更多
登录 后发表回答