0

I have a generic method which compares 2 properties, if value are different it logs the changes and Saves.

    private void SaveIfChanged<T>(Expression<Func<T>> expression, T newValue)
            {

                var expr = (MemberExpression)expression.Body; 
                var obj = (MemberExpression)expr.Expression; 
                var fieldsOfObj = (ConstantExpression)obj.Expression;
                var valuesOfAllFieldsOfObj = ((FieldInfo)obj.Member).GetValue(fieldsOfObj.Value); 

                var propertyInfo = ((PropertyInfo)expr.Member); 
                var oldPropertyValue = propertyInfo.GetValue(valuesOfAllFieldsOfObj, null); 

                if (oldPropertyValue.Equals(newValue)) return;       


 var desctiptionAttributes = (DescriptionAttribute[])propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
                Log("{0} changed from {1} to {2}",desctiptionAttributes[0].Description, oldPropertyValue, newValue);
                propertyInfo.SetValue(valuesOfAllFieldsOfObj, newValue, null);

                Save();
            }

It works fine when I pass Properties which are members of a non-static class, but when I pass a static property then it doesn't work.

SaveIfChanged(() => _settings.DomainName, DomainName); // Works
SaveIfChanged(() => Settings.DomainName, DomainName);  //Doesn't work

I also know how to get fields/properties of static class, but only when I have the class name. I just don't know how can I combine the following with my method.

        Type s= typeof(Settings);
        FieldInfo[] fields = t.GetFields(BindingFlags.Static | BindingFlags.Public);

        foreach (FieldInfo fi in fields)
        {
            Console.WriteLine(fi.Name);
            Console.WriteLine(fi.GetValue(null).ToString());
        }

Thank you.

Marshal
  • 6,551
  • 13
  • 55
  • 91

2 Answers2

2

The problem is when you try to access property Expression in var fieldsOfObj = (ConstantExpression)obj.Expression;.

MemberExpression is mainly composed of two properties:

  • Expression gets the containing object of the field or property.
  • Member gets the field or property to be accessed.

In the first case (_settings.DomainName) the property Expression gets a memberExpression object containing _settings, and property Member returns a MemberInfo pointing to DomainName.

In the second case (Settings.DomainName), the property Expression returns null because you are not accessing the property of an instance, you are accessing a static property. In the code the object obj is null, and that's when the problem comes.

You can see this question for details.

To solve this issue, you can do this:

private static void SaveIfChanged<T>(Expression<Func<T>> expression, T newValue)
{
    var expr = (MemberExpression)expression.Body;

    object valuesOfAllFieldsOfObj = null;
    if (expr.Expression != null)
    {
        var obj = (MemberExpression)expr.Expression;
        var fieldsOfObj = (ConstantExpression)obj.Expression;
        valuesOfAllFieldsOfObj = ((FieldInfo)obj.Member).GetValue(fieldsOfObj.Value);
    }

    var propertyInfo = ((PropertyInfo)expr.Member);
    var oldPropertyValue = propertyInfo.GetValue(valuesOfAllFieldsOfObj, null);

    if (oldPropertyValue.Equals(newValue)) return;

    var desctiptionAttributes = (DescriptionAttribute[])propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
    Log("{0} changed from {1} to {2}", desctiptionAttributes[0].Description, oldPropertyValue, newValue);
    propertyInfo.SetValue(valuesOfAllFieldsOfObj, newValue, null);

    Save();
}
Community
  • 1
  • 1
Arturo Menchaca
  • 15,783
  • 1
  • 29
  • 53
1

Allowing Expression<Func<T>> expression will allow your method to be called with anything. To be really flexible, why not compile and call the method? then you dont need any of this magic...

if (expression.Compile()() != newValue)
{
    ....
}
MBoros
  • 1,090
  • 7
  • 19