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