Hugh Hunter

Hacking his way through the weeds, the.dudeabid.es

Using Reflection to Execute Accessors for Property Names Stored in a Variable

| Comments

I had a task to create an export process that would prepare some data to be sent over to an external vendor. The only catch was that I expected a lot of fields in my database to contain data that was longer than the external vendor to accept. I needed to do some simple length validatinos for every field that I was exporting.

From my limited experience using DataAnnotations in my .Net MVC projects, I knew this was the best wy to accomplish this. But how to use DataAnnotations in a non-web application? So the first challenge was to figure out how to run the validations since they’re run automatically in the context of a web request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.ComponentModel.DataAnnotations;

...snip...

[StringLength(35)]
public string Address1 { get; set; } //LEN 35
[StringLength(35)]
public string Address2 { get; set; } //LEN 35

private bool TryValidate(out List<ValidationResult> results)
{
    var context = new ValidationContext(this, serviceProvider: null, items: null);
    results = new List<ValidationResult>();
    return Validator.TryValidateObject(this, context, results, true);
}

Not so bad. But what do you do with this List business?

The next challenge was to try to fix-up the data that failed the length validations. The example below fixes some common verbosity in addresses that was safe to do to try to get them within the 35 character limit.

Then, using reflection, I was able to update the object being exported in a generic way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
private void Fixup(List<ValidationResult> results)
{
    foreach (ValidationResult result in results)
    {
        \\ the name of the key that failed
        string key = result.MemberNames.First();

        var type = this.GetType();
        var prop = type.GetProperty(key);

        if (Regex.IsMatch(key, "Address"))
        {
            // the value that failed validation
            var val = prop.GetValue(this, null).ToString();

            // do our best to shorten this field for ourselves
            val = val.Replace("Street", "St");
            val = val.Replace("Avenue", "Ave");
            val = val.Replace("Floor", "Fl");
            val = val.Replace("Suite", "Ste");
            val = val.Replace("Drive", "Dr");
            val = val.Replace("Boulevard", "Blvd");
            val = val.Replace("Apartment", "Apt");

            // set the property on the object
            prop.SetValue(this, val, null);
        }
    }

    results.Clear();
    if (!this.TryValidate(out results))
    {
        // log that we failed to fixup a record --- manual intervention required
    }
}

Comments