Some background to understand the code. I have an MVC application, all my models implement IModel. IModel just enforces to have an int Id property.
The following method "updates" an instances of a model with the data available in a viewmodel. For each property of the viewmodel it checks if a corresponding property exists in the model, if it does It updates the value in the model with those of the viewmodel, if the values are different.
At the last point it goes wrong. The statement : OldValue != NewValue always returns true, even if f.e. both are integers, 1. Why ?
public static Boolean UpdateIfChanged<M, VM>(this M Model, VM ViewModel) where M : IModel
{
Boolean HasUpdates = false;
Type Mtype = typeof(M);
PropertyInfo[] MProperties = Mtype.GetProperties(BindingFlags.Public | BindingFlags.Instance);
Type VMtype = typeof(VM);
PropertyInfo[] VMProperties = VMtype.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var VMProperty in VMProperties)
{
if (!VMProperty.PropertyType.GetInterfaces().Any(x => x.Name == typeof(IModel).Name)
&& MProperties.Any(x => x.Name == VMProperty.Name)
&& Mtype.GetProperty(VMProperty.Name).PropertyType == VMProperty.PropertyType )
{
var OldValue = Mtype.GetProperty(VMProperty.Name).GetValue(Model);
var NewValue = VMtype.GetProperty(VMProperty.Name).GetValue(ViewModel);
if (NewValue != null)
{
if (OldValue == null)
{
Mtype.GetProperty(VMProperty.Name).SetValue(Model, NewValue);
HasUpdates = true;
}
else
{
if (OldValue != NewValue)
{
Mtype.GetProperty(VMProperty.Name).SetValue(Model, NewValue);
HasUpdates = true;
}
}
}
}
}
return HasUpdates;
}
The problem here is that OldValue and NewValue are objects at compile time, not int, and therefore the ==/!= operators call the ones defined by the object class, because all operators are static. The object operators check for referential equality, not logical equality, and thus only check if the two arguments are exactly the same object (the same pointer in C++)
To get around this, you have several options
As Tigran mentioned, type cast the values coming out, so you end up using the operator defined in int instead of object
Call object.Equals(OldValue, NewValue), which checks for null and then calls the virtual Object.Equals(object o), which thus will call the function defined in the actual class/struct of the calling object (in this case int)
your GetValue(..) call returns boxed integer object, so reference type.
Hence your code:
//OldValue and NewValue are Object types and NOT integers !
if (OldValue != NewValue)
compares references and not values.
You didn't notice that as you are using var keyword, which "hides" concrete type.
To correctly overcome this issue, may do like:
....
var OldValue = (int)Mtype.GetProperty(VMProperty.Name).GetValue(Model); //cast to int
var NewValue = (int)VMtype.GetProperty(VMProperty.Name).GetValue(ViewModel);//cast to int
....
Try this if (OldValue.ToString() != NewValue.ToString()). Or if OldValue/NewValue are int-s: if ((int)OldValue != (int)NewValue)
A better syntax would be:
if (!Equals(OldValue, NewValue))
Related
I have a CreateMap:
CreateMap<ExtractInsertRequestViewModel, Extract>()
.ConstructUsing(source => new Extract(source.MemberId,
source.IsChargeBack,
source.Type,
source.Description,
source.Value,
source.VerifyBalanceBeforeRedeem))
Then I execute the transfomation (vm is my viewmodel):
var extract = Mapper.Map<Extract>(vm);
But inside my Extract ctor, I have 2 functions that may change the values (SetStatus and SetValue):
public Extract(int memberId, bool isChargeBack, ExtractType type, string description, int value, bool verifyBalanceBeforeRedeem)
{
MemberId = memberId;
IsChargeBack = isChargeBack;
Type = type;
Description = description;
SetStatus(Status.A);
IsAble = true;
VerifyBalanceBeforeRedeem = verifyBalanceBeforeRedeem;
SetValue(value);
}
public void SetValue(int newValue)
{
if (Type == ExtractType.DEBIT && newValue > 0 || Type == ExtractType.CREDIT && newValue < 0)
newValue = newValue * -1;
Value = newValue;
}
The issue is, after the value was changed by any of this functions, the Extract object result is created with the original values.
For ex: I passed a Debit operation with a positive value. The SetValue method should convert to negative, but in the final object, the value still positive.
Seems to me that the ConstructUsing runs after the constructor. The constructor set the correct values but ConstructUsing overwrite the values with the original ones.
Any thoughts of what's going on here ??
Thanks in advance!
I need to check if two types are the same:
private bool AreOfTheSameType(object obj1, object obj2) {
return obj1.GetType()==obj2.GetType();
}
This works fine with this values:
var test1=AreOfTheSameType(new DateTime(), new DateTime()) // true;
var test2=AreOfTheSameType(new int(), new DateTime()) // false;
What I now want is that the following returns true, too:
var test3=AreOfTheSameType(new int?(), new int())
So if the types have the same base, but one is nullable, the other isn't, I also want to return it as true. Or to say it in another way I want to have a function that returns whether I can store obj1 into obj2 directly using reflection without having to cast the value.
UPDATE
I reduced my code to make it more readable. Looks like this time that was contra-productive. The real-world-code follows:
var entity = Activator.CreateInstance(typeof(T));
Type entityType = typeof(T);
PropertyInfo[] entityProperties = entityType.GetProperties();
foreach (KeyValuePair<string, object> fieldValue in item.FieldValues)
{
if (fieldValue.Value == null) continue;
var property = entityProperties.FirstOrDefault(prop => prop.Name == fieldValue.Key);
if (property != null && property.CanWrite)
{
Type valueType = fieldValue.Value.GetType();
if (fieldValue.Value.GetType() == property.PropertyType) {
// Assign
}
}
}
The problem on the "//Assign" - line is, I have the following two types:
fieldValue.Value.GetType().ToString()="System.DateTime"
property.PropertyType.ToString()="System.Nullable`1[System.DateTime]"
which are obiously not the same but could be assigned
which are obiously not the same but could be assigned
It looks like you're after the Type.IsAssignableFrom method:
var dt = typeof(DateTime);
var nd = typeof(DateTime?);
Console.WriteLine(dt.IsAssignableFrom(nd)); // false
Console.WriteLine(nd.IsAssignableFrom(dt)); //true
Live example: http://rextester.com/KDOW15238
Calling GetType on nullable types returns the original type:
int? i = 5;
Type t = i.GetType();
Console.WriteLine(t.FullName); //"System.Int32"
So AreOfTheSameType((int?)5, 5) should return true.
But as soon as you box a Nullable<T>, you either get null (if Nullable<T>.HasValue was false), or you get the boxed underlying value, losing the "nullable" part. So the problem you're facing here is that new int? will be boxed into a null object when passed to the method.
The problem with nullable types is that empty values box to null, and once they're null, you cannot find out what they were. The only way to solve this, then, is with generics:
private bool AreOfTheSameType<T1,T2>(T1 obj1, T2 obj2) {
// get the types as best we can determine
var t1 = obj1?.GetType() ?? typeof(T1);
var t2 = obj2?.GetType() ?? typeof(T2);
// Nullable<T> => T
t1 = Nullable.GetUnderlyingType(t1) ?? t1;
t2 = Nullable.GetUnderlyingType(t2) ?? t2;
// compare
return t1 == t2;
}
This will use the object if available (to allow for subclasses etc), but will fall back to typeof if the object is null - which means it should work for int? etc... as long as the type of the expression being passed in wasn't object to begin with; if it was object and the value is null, then... you're out of luck - you can't ever find out the original type. By which I mean:
This should be fine:
var test3=AreOfTheSameType(new int?(), new int());
But this will fail:
object x = new int?(), y = new int();
var test4=AreOfTheSameType(x, y);
I'm using WCF RIA services to do a few small pieces of a web application; mainly populate/filter lists (I don't understand RIA well enough yet to trust I'm doing server side validation correct). One of the things I do is get a list of which fields have which generic type, by which I mean, strings are a text type, decimal, double, integer are numeric, etc. I do that with a LINQ query
Fields = type.GetProperties().Where(pi => pi.PropertyType == typeof(string) && pi.GetGetMethod() != null && pi.Name != "DisplayName")
.Select(pi => new FieldData
{
FieldName = CommonResources.AddSpacesToSentence(pi.Name, true),
FieldType = "Text"
}).....
The field DisplayName is a special field that should be ignored in lists, but as this application is growing I realize this isn't a very maintainable/expandable/buzzwordable way to go about this. What I really want is to know is if metadata for the DisplayName property has the attribute [Display(AutoGenerateField = false)]
Is there a way I can check for that in my LINQ?
Update:
After posting this I was able to slowly work out how to do this (I've never worked with Attributes in this way before). The answer given by King King looks nice and is very generic, but the way I wound up solving this was different, so if you're interested in another way, here's what I found. I added this to the LINQ query:
((DisplayAttribute)Attribute.GetCustomAttribute(pi, typeof(DisplayAttribute))).GetAutoGenerateField() == false
You can use the GetCustomAttributes method to filter properties with the given attribute:
...
.Where(pi => pi.GetCustomAttributes(typeof(DisplayAttribute), true).Any())
...
The true argument includes inheritance in attribute search.
You can try the following code the check against an attribute value of a property:
public bool CheckPropertyAttribute(Type type, string property,
Type attributeType, string attProp, object value)
{
var prop = type.GetProperty(property);
if (prop == null) return false;
return CheckPropertyAttribute(prop, attributeType, attProp, value);
}
public bool CheckPropertyAttribute(PropertyInfo prop, Type attributeType,
string attProp, object value){
var att = prop.GetCustomAttributes(attributeType, true);
if (att == null||!att.Any()) return false;
var attProperty = attributeType.GetProperty(attProp);
if (attProperty == null) return false;
return object.Equals(attProperty.GetValue(att[0], null),value);
}
Usage::
if(CheckPropertyAttribute(pi, typeof(DisplayAttribute), "AutoGenerateField", false)){
//...
}
NOTE: I provided 2 overloads, but in your case I think you just need to use the second overload (the case in which we already have some PropertyInfo).
Well, I need to repeat same code for many properties.
I've seen examples taking Action delegates, but they don't fit quite well here.
I want something like this: (see explanation below)
Dictionary<Property, object> PropertyCorrectValues;
public bool CheckValue(Property P) { return P.Value == PropertyCorrectValues[P]; }
public void DoCorrection(Property P) { P.Value = PropertyCorrectValues[P]; }
.
I want to have a dictionary containing many properties and their respective "correct" values. (I know it's not well declared, but that's the idea). Properties are not necessarely inside my class, some of them are in objects of different assemblies.
A method bool CheckValue(Property). This method must access the actual value of the property and compare to the correct value.
And a method a void DoCorrection(Property). This one sets the property value to the correct value.
Remember I have many of those properties, I wouldn't like to call the methods by hand for each property. I'd rather iterate through the dicionary in a foreach statement.
So, the main question is in the title.
I've tried the by ref, but properties don't accept that.
Am I obligated to use reflection??? Or is there another option (if I need, reflection answer will be accepted as well).
Is there anyway I can make a dictionary with pointers in C#? Or some kind of assignment that changes the value of variable's target instead of changing the target to another value?
Thanks for the help.
You can do this using reflection. Get a list of the properties on the object of interest with typeof(Foo).GetProperties(). Your PropertyCorrectValues property can have type IDictionary<PropertyInfo, object>. Then use the GetValue and SetValue methods on PropertyInfo to perform the desired operations:
public bool CheckProperty(object myObjectToBeChecked, PropertyInfo p)
{
return p.GetValue(myObjectToBeChecked, null).Equals(PropertyCorrectValues[p]);
}
public void DoCorrection(object myObjectToBeCorrected, PropertyInfo p)
{
p.SetValue(myObjectToBeCorrected, PropertyCorrectValues[p]);
}
In addition to Ben's code I'd like to contribute the following code fragment:
Dictionary<string,object> PropertyCorrectValues = new Dictionary<string,object>();
PropertyCorrectValues["UserName"] = "Pete"; // propertyName
PropertyCorrectValues["SomeClass.AccountData"] = "XYZ"; // className.propertyName
public void CheckAndCorrectProperties(object obj) {
if (obj == null) { return; }
// find all properties for given object that need to be checked
var checkableProps = from props
in obj.GetType().GetProperties()
from corr in PropertyCorrectValues
where (corr.Key.Contains(".") == false && props.Name == corr.Key) // propertyName
|| (corr.Key.Contains(".") == true && corr.Key.StartsWith(props.DeclaringType.Name + ".") && corr.Key.EndsWith("." + props.Name)) // className.propertyName
select new { Property = props, Key = corr.Key };
foreach (var pInfo in checkableProps) {
object propValue = pInfo.Property.GetValue(obj, null);
object expectedValue = PropertyCorrectValues[pInfo.Key];
// checking for equal value
if (((propValue == null) && (expectedValue != null)) || (propValue.Equals(expectedValue) == false)) {
// setting value
pInfo.Property.SetValue(obj, expectedValue, null);
}
}
}
When using this "automatic" value correction you might also consider:
You cannot create a PropertyInfo object just by knowing the property name and independently of the declaring class; that's why I chose string for the key.
When using the same property name in different classes then you might need to change the code that is doing the actual assignment because the type between the correct value and the property type might differ.
Using the same property name in different classes will always perform the same check (see point above), so you might need a syntax for property names to restrict it to a specific class (simple dot notation, doesn't work for namespaces or inner classes, but might be extended to do so)
If needed you can replace the "check" and "assign" part with separate method calls, but it might be done inside the code block as stated in my example code.
I'm trying to write an extension method that I can use to copy values from one object property to another object of a different type, as long as the property names and types match exactly.
This is what I have:
public static T CopyFrom<T>(this T toObject, object fromObject)
{
var fromObjectType = fromObject.GetType();
var fromProperties = fromObjectType.GetProperties();
foreach (PropertyInfo toProperty in toObject.GetType().GetProperties())
{
PropertyInfo fromProperty = fromObjectType.GetProperty(toProperty.Name);
if (fromProperty != null) // match found
{
// check types
var fromType = fromProperty.PropertyType.UnderlyingSystemType;
var toType = toProperty.PropertyType.UnderlyingSystemType;
if (toType.IsAssignableFrom(fromType))
{
toProperty.SetValue(toObject, fromProperty.GetValue(fromObject, null), null);
}
}
}
return toObject;
}
This is working great for non boxed types, but Nullable<T> returns false when I call
toType.IsAssignableFrom(fromType)
because its type is Nullable<T> and is not the underlying type T.
I read here that GetType() should unbox the Nullable<T> so it returns T but if I call that on PropertyInfo.PropertyType I get ReflectedMemberInfo and not the type T im looking for.
I think I'm missing something obvious here, so I thought I would throw it open to SO to get some advice.
Anyone have any ideas?
UPDATE: Here is the final method for anyone searching for this.
public static T CopyFrom<T>(this T toObject, object fromObject)
{
var fromObjectType = fromObject.GetType();
foreach (PropertyInfo toProperty in toObject.GetType().GetProperties())
{
PropertyInfo fromProperty = fromObjectType.GetProperty(toProperty.Name);
if (fromProperty != null) // match found
{
// check types
var fromType = Nullable.GetUnderlyingType(fromProperty.PropertyType) ?? fromProperty.PropertyType;
var toType = Nullable.GetUnderlyingType(toProperty.PropertyType) ?? toProperty.PropertyType;
if (toType.IsAssignableFrom(fromType))
{
toProperty.SetValue(toObject, fromProperty.GetValue(fromObject, null), null);
}
}
}
return toObject;
}
You're looking for Nullable.GetUnderlyingType.
For example:
toType = Nullable.GetUnderlyingType(toType) ?? toType;