简体   繁体   中英

How to recursively null check the nested object's required properties using reflection

Here are my classes with some required properties,

public class Employee
{
    public EmployeeType employeeType {get; set;}
    public Description description {get; set;}
}

public class EmployeeType
{
    public NameType nameField {get; set;}
    public string Address {get; set;}
}

public class NameType 
{
    [Required]
    public string FirstName {get; set;}
    [Required]
    public string LastName {get; set;}
    public Role RoleType {get; set;}
}

public class Role 
{
    public int RoleId {get; set;}
    [Required]
    public string Name {get; set;}
}

public class Description
{
    [Required]
    public string ShortDesciption {get; set;}
    public string LongDescription {get; set;}
}

I have written a method to do a recursive check of properties but it does not go by Required annotation and it would not keep moving forward after finding first property with null value.

Here is my code,

private List<string> GetNullOrEmptyPropertiesList(object myObject)
{
    List<string> lst = new List<string>();
    foreach (PropertyInfo pi in myObject.GetType().GetProperties())
    {
        if (pi.PropertyType == typeof(string))
        {
            string value = (string)pi.GetValue(myObject);
            if (string.IsNullOrEmpty(value))
            {
                if (pi.Name == "ShortDescription" || pi.Name == "FirstName")
                {
                    lst.Add(pi.Name);
                }
            }
         }

         else
         {
             var value = pi.GetValue(myObject);
             return GetNullOrEmptyPropertiesList(value);
         }
     }

     return lst;
 }

you're not the first who does stuff like this. MVC has a way of validating objects without you having to do reflection.

I had to do some code generation, generate viewmodels, generate validation rules and generate code for tests to make sure everything was running correctly.

MVC has ModelState and IsValid to check these things so it would make sense to allow you to validate things directly.

Here's an example which validates just one property in a model :

var context = new ValidationContext(model, serviceProvider: null, items: null);
            context.MemberName = "FirstName";

            var results = new List<ValidationResult>();
            var modelValue = "someValue";
            var isValid = Validator.TryValidateProperty(modelValue, context, results);

here, the model is the class which I want to validate, MemberName is the property I want to validate and modelValue is the data for that property. This will check all the validation rules just like the MVC engine does.

if you want to validate a whole model, meaning more than just one property then you can use something like :

var model = new SomeClassIWantToValidate;
var context = new ValidationContext(model, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(model, context, results, true);

Pay extra attention to the true parameter at the end. It says validate all my properties basically.

Here is how you can do what you want:

private static List<string> GetNullOrEmptyPropertiesList(object myObject)
{
    var list = new List<string>();
    AddNullOrEmptyProperties(myObject, list);
    return list;
}

private static void AddNullOrEmptyProperties(object myObject, List<string> list, string path = null)
{
    if (myObject == null) return;
    var type = myObject.GetType();
    if (type.IsPrimitive || type == typeof(string)) return;
    foreach (var pi in myObject.GetType().GetProperties())
    {
        if (!pi.CanRead || pi.GetIndexParameters().Length > 0) continue;
        var name = path != null ? path + "." + pi.Name : pi.Name;
        var value = pi.GetValue(myObject);
        if (pi.IsDefined(typeof(RequiredAttribute)) && (value == null || (value as string) == string.Empty))
            list.Add(name);
        AddNullOrEmptyProperties(value, list, name);
    }
}

A few notes:

  • Anytime you need to populate some list recursively, it's good to separate the recursive part from non recursive part. In non recursive part you just allocate the list and call the recursive part passing the allocated list and any additional parameters needed.

  • The easiest way of to check for a presence of a specific attribute is to use some of the IsDefined methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM