I have method that updates the properties of a class and then updates that object in the database using a web service. I want to verify that the update succeeded by pulling the new object and comparing it to an object in memory.
Do I need to compare each property or will this determine if they have the same values for all the properties?
var areEqual = objectThatHasChanges.Equals(objectSavedToDatabase);
You will need to compare each property. If these are two reference-type object the equals method will just use default equal implementation which will check the objects are the same instances:
https://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx
If the current instance is a reference type, the Equals(Object) method
tests for reference equality, and a call to the Equals(Object) method
is equivalent to a call to the ReferenceEquals method. Reference
equality means that the object variables that are compared refer to
the same object. The following example illustrates the result of such
a comparison. It defines a Person class, which is a reference type,
and calls the Person class constructor to instantiate two new Person
objects, person1a and person2, which have the same value. It also
assigns person1a to another object variable, person1b. As the output
from the example shows, person1a and person1b are equal because they
reference the same object. However, person1a and person2 are not
equal, although they have the same value.
You should either overload the "Equals" method and write your own which compare each or key properties (like last modify date, revision etc..) or you should write a method which will take these two objects and compare them.
If you have a lot of fields in different objects you can write generic method which uses reflection to iterate through all properties in object and compares with the other. Of course checking first if both objects are of the same type.
If the property names are the same, this is more or less trivial:
private static bool IsEqual(Model1 model, Model2 model2)
{
long changes = 0;
foreach (var pi in typeof(Model1).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var secondModelPi = typeof(Model2).GetProperty(pi.Name, BindingFlags.Instance | BindingFlags.Public);
if (secondModelPi != null)
{
if (!pi.GetValue(model).Equals(secondModelPi.GetValue(model2)))
{
changes++;
}
}
}
return changes == 0;
}
If you really want to check if update is successful then do something in this way:
string query = #"UPDATE [Entities] SET [Value] = [Value] + 1";
// SQL initialization
return (sqlCommand.ExecuteNonQuery() > 0);
ExecuteNonQuery returns the number of affected \ inserted rows. Thus, positive result means that update was successful.
If you, for some reasons, want to get entity from server after update then use the following approach:
string query = #"UPDATE [Entities] SET [Value] = [Value] + 1;
SELECT * FROM [Entities] WHERE [Id] = SCOPE_IDENTITY()"
// SQL initialization
var reader = sqlCommand.ExecuteReader();
while (reader.Read())
{
// Object initialization
}
These ways are better in terms of convenience and performance.
To expound upon #zaitsman's answer, if you want to reuse this for multiple different types you would need to use generic types. I also converted the class to an extension method, as in my mind this should work similar to a LINQ query or .Equals.
//'this' Makes this an extension method if you do not want this to be an extension method just remove the 'this' keyword before model
private static bool IsSame<T,T2>(this T model, T2 model2)
{
long changes = 0;
foreach (var pi in typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var secondModelPi = typeof(T2).GetProperty(pi.Name, BindingFlags.Instance | BindingFlags.Public);
if (secondModelPi != null)
{
if (!pi.GetValue(model).Equals(secondModelPi.GetValue(model2)))
{
changes++;
}
}
}
return changes == 0;
}
Example Usage
model.IsSame(model2);
Related
I'm fairly new to unit testing. The following code is just for reference.
I want to check empId of list one is same as the emp id of list 2 or not.
public class EmpInfo
{
public EmpInfo( string lastName,string firstName, string empId)
{
EAlphabeticLastName = lastName;
EFirstName = firstName;
EmpId = empId;
}
}
[Test]
[Category("Explicit")]
public void testEmp()
{
public List<EmpInfo> List1e = new List<EmpInfo>(){
new EmpInfo("dx","Tex","25")
};
public List<EmpInfo> List2e = new List<EmpInfo>(){
new EmpInfo("dx","Tex","25")
};
Assert.AreEqual(List1e.empId,List2e.empId);
}
What is the correct way to check equality of list items in Nunit (C#) ?
You may have to override the Equals and GetHashCode method in the class EmpInfo and write the compare logic .
Use the above methods to check whether all the objects in one list are present in another .
There are numerous ways to achieve it
Use https://fluentassertions.com/objectgraphs/ (The easiest and fastest way)
List1e.Should().BeEquivalentTo(List2e);
Move all the individual comparisons to the .Equals method (Or implement IEqualityComparer)
Build a helper method that iterates through public properties by reflection and assert each property
public static void PropertyValuesAreEquals(object actual, object expected) {
PropertyInfo[] properties = expected.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
object expectedValue = property.GetValue(expected, null);
object actualValue = property.GetValue(actual, null);
if (!Equals(expectedValue, actualValue))
Assert.Fail("Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
//……………………………….
}
Use JSON to compare the object’s data
public static void AreEqualByJson(object expected, object actual)
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var expectedJson = serializer.Serialize(expected);
var actualJson = serializer.Serialize(actual);
Assert.AreEqual(expectedJson, actualJson);
}
Use Property Constraints (NUnit 2.4.2)
How about Assert.IsTrue(List1e.SequenceEqual(List2e))
Alternatives to checking that the list contains only items with the same ID:
Implement equality for the EmpInfo class, as suggested by User965207, either by overriding Equals or implementing the IEquatable interface. NUnit will use either one if present. If you don't have control over the code of the system under test, this is of course not available to you.
Use a Select statement to create a new list for comparison, as suggested by Sean. You actually only need to do this for the actual value being tested. Your expected value can just be a list or array of empids in the first place. This approach only requires changing the test code.
Use NUnit's List class to do essentially the same as option 2 for you...
Assert.That(List.Map(List1e).Property("empid"),
Is.EqualTo(new [] {"empid1", "empid2" /etc/}));
Since you have not provided a full implementation of EmpInfo, I made the assumption that empid is a property. If it's a field, this won't work.
I realize I haven't added a lot to the prior answers here, but I thought a summary of the possible approaches with pros and cons would be of help.
public class Sample
{
public int Id { get; set; }
public string Description { get; set; }
public DateTime EffectiveDate { get; set; }
}
IEnumerable<Sample> sampleList;
//Populate the list
Now I want to filter the list some times by "Id" property, sometimes "Description" property. Just want to pass the property name (filterColumn) and property value (filterValue) both as strings.
I tried the following:
IEnumerable<Sample> result = sampleList.Where(x => x.GetType().GetProperty(filterColumn).Name == filterValue);
and
string whereQuery = string.Format(" {0} = \"{1}\"", filterColumn, filterValue);
IEnumerable<Sample> result = sampleList.AsQueryable().Where(whereQuery);
The second option works, if i pass the filterColumn as "Description", but throws incompatible '=' operator between string and int error when "Id" is passed as filterColumn and some filterValue like "1".
Appreciate any help. Thanks
Your first approach can work. Expanding on Jon Skeet's comment, here's the adjusted statement.
IEnumerable<Sample> result = sampleList.Where(
x => x.GetType().GetProperty(filterColumn).GetValue(x, null).Equals(filterValue)
);
To put a little context around this, you would have to allow for the differing data types. You can do this at least two ways: use a generic method or use the object data type. For illustrative purposes, I'll use the object approach.
public IEnumerable<Sample> GetFiltered(
IEnumerable<Sample> samples, string filtercolumn, object filtervalue
{
return samples.Where(
x => x.GetType().GetProperty(filtercolumn).GetValue(x, null).Equals(filtervalue)
);
}
IEnumerable<Sample> sampleList;
var byId = GetFiltered(sampleList, "Id", 100);
var byDescription = GetFiltered(sampleList, "Description", "Some Value");
This example is not really safe as there is no type checking to ensure that the property value will be of the same data type that you are passing in. For example, there is nothing stopping you from passing "Description" and 100 as parameters. You can't do a meaningful comparison between an integer and a string so you will always come up with an empty result. The Equals method does not throw an exception, it just sees that the two objects are different. As Jon pointed out, you'll always want to use Equals in this case rather than the "==" operator. The Equals method is intended to compare content while "==" compares references. Example:
Console.WriteLine(12 == 12);
// True
object a = 12;
object b = 12;
Console.WriteLine(a == b);
// False - because, due to boxing, a and b are separate objects
// that happen to contain the same value. (Check out "boxing"
// if this doesn't make sense.)
Console.WriteLine(a.Equals(b));
// True - because the Equals method compares content (value)
Also, note that strings have some special behaviors when using the "==" operator. The important thing to remember is that there is a difference between references (containers) and content. You want to compare content and that means Equals. (I have noticed that the Immediate window in Visual Studio is inconsistent in its results regarding strings when using "==". I suspect this is because string references can be, but are not always, optimized in that window.)
You state that your second approach works. I have not seen this type of filter string in a standard IEnumerable.Where method. So I am guessing that you are using some extensions. Your example does not work as shown. The DataTable class uses filter strings that match your usage. In general, a filter string has to be constructed in different ways based on data type. For example, a string requires the quotes (which you have) but an integer value does not use quotes.
Another option that you have is to set up a dictionary with the required operations.
public IEnumerable<Sample> GetFiltered(
IEnumerable<Sample> samples, string property, string value)
{
var map = new Dictionary<string, Func<string, Func<Sample, bool>>>()
{
{ "Description", v => s => s.Description == v },
{ "Id", v => s => s.Id == int.Parse(v) },
};
return samples.Where(map[property](value));
}
The advantage here is that you can perform a more complex comparison, such as adding custom filters by ranges of values, or those containing more than one property.
So I have 2 classes, both have identical Property names. One class contains different variables: int, strings, bool and DateTime The second class contains only 1 int and the rest are all strings.
Now I want to loop through all the properties, get the value from class1, encrypt that data and save it as a string in obj2, then return it to the main form (to save it in a database later).
public PersoonEncrypted EncryptPersonClass(Class1 object1)
{
PersoonEncrypted persEncrypt = new PersoonEncrypted(); //second class obj
Type type = object1.GetType();
PropertyInfo[] properties = type.GetProperties();
Type type2 = persEncrypt.GetType();
PropertyInfo[] properties2 = type.GetProperties();
foreach (var bothProperties in properties.Zip(properties2, (obj1, obj2) => new { Obj1 = obj1, Obj2 = obj2 }))
{
string value = "";
value = bothProperties.Obj1.GetValue(object1) as string;
if (!string.IsNullOrWhiteSpace(value))
{
string encryptValue = Encrypt(value);
if ((bothProperties.Obj2 != null) && (bothProperties.Obj2.PropertyType == typeof(string)))
{ //!= null check has no effect at all
bothProperties.Obj2.SetValue(persEncrypt, encryptValue, null); //errorLine
}
}
}
return persEncrypt;
}
That is what I came up with until now.
I have, of course, searched for other solutions like this one. This, after applying some own changes, didn't return any errors, but it didn't save any encrypted strings into the class persEncrypt. What I concluded was, from that test, is that it was testing if the value in the second class(persEncrypt in my example) from the particular property was null, while it shouldn't do that, it should make a new instance of that variable and save it in the object class, but removing that check gave me the same error.
you're just .Zip-ing the two lists of PropertyInfo objects, which simply iterates through both lists and doesn't check or sort for any sort of matching. This could result in erroneous behavior depending on the order in which properties appear - consider using a .Join instead to match property names.
This code doesn't check for an indexer on the property before attempting to assign to it without one - any indexed property which is of type string will make it to this point and then throw an exception when you try to set it.
Because this code is calling into Properties, there's the possibility an exception is being thrown by the code of the Property itself. This is where a StackTrace from your exception could reveal much more about what's happening.
Your code also checks for a property of type string directly - when using reflection you should use IsAssignableFrom instead in order to allow for inherited types, though that is unlikely the issue in this one case.
I have a requirement to copy ONLY populated values from one object that are not already populated in another object of the same type.
For example we are passed an object, it is only partially instantiated with data, we read the database to get a fully instantiated version of the object – however this may not have changes by the application committed to the database yet – hence we need to move any values from the database version into the passed in version of the object – without overwriting any values that may already exist in the passed in object (as these are the most up to date values).
The code below suggested by Adam Robinson in another post (see below very useful – thanks!) is a good starting point. However I need to extend this – as I only want to copy over values that are NOT already populated on the target object (i.e. need to check the destProperty is not null). However as an added complication, there are internal complex types declared within the object passed in, this code copies the high level sub groups over without going into the individual properties of the sub groups (i.e. any variables declared with the Root cdt I can try and check for null, but all the fields in sub cdts are simply copied over without going through the individual fields).
Any help would be greatly appreciated.
public static void CopyPropertyValues(object source, object destination)
{
var destProperties = destination.GetType().GetProperties();
foreach (var sourceProperty in source.GetType().GetProperties())
{
foreach (var destProperty in destProperties)
{
if (destProperty.Name == sourceProperty.Name &&
destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
destProperty.SetValue(destination, sourceProperty.GetValue(
source, new object[] { }), new object[] { });
break;
}
}
}
}
First you need to determine what populated means in your book.
Once you determine that, write you own version of the following IsDefaultValue method.
What I have written will answer with true in the following manner:
if it's a bool then it needs to have the false value
if it's an int then it needs to be 0
if it's any class then needs to be null
etc
So here's my version of the method:
public static bool IsDefaultValue(object #object) {
if (null == #object)
return true;
else if (#object.GetType().IsValueType) {
var isDefault = Activator.CreateInstance(#object.GetType()).Equals(#object);
return isDefault;
} else
return false;
}
Then, presuming that you're interested only in the non indexer properties, that you have no hierarchies and that you will always call this method with objects of the same type,
you could just filter out those properties which are not default in the source but are default in the destination.
Then you could traverse the instance graph recursively for those cases when you have a non-default value in your destination properties.
Please not that what I have written here is just a demonstration of how you might be able to accomplish your task. There are intricate details in your particular scenario which you need to address yourself as they are not obvious from you question.
For instance, I assumed it would be a good idea to add a stop condition for the recursive traversal (the remainingDepth parameter)
public static void CopyPropertyValues(object source, object destination, int remainingDepth = 3) {
// we've reached the farthest point we're allowed to go to
// anything beyond this point won't be affected by this method
if (remainingDepth == 0)
return;
// just a check to make sure the following lines won't backfire
if ((null == source) || (null == destination))
throw new ArgumentNullException();
// we'll need to also check that the 2 objects are of the same type
var type = source.GetType();
if (destination.GetType() != type)
throw new ArgumentException("The two objects should be of the same type");
var properties = type.GetProperties()
// just filter out the properties which are indexers (if any)
// and also those properties which are read or write only
.Where(property => (property.GetIndexParameters().Length == 0) &&
property.CanRead && property.CanWrite);
foreach (var property in properties) {
var sourceValue = property.GetValue(source, null);
var destValue = property.GetValue(destination, null);
if (!IsDefaultValue(sourceValue))
if (IsDefaultValue(destValue))
property.SetValue(destination, sourceValue, null);
else
if (sourceValue.GetType() == destValue.GetType())
CopyPropertyValues(sourceValue, destValue, remainingDepth - 1);
}
}
Please note that property enumeration is needed just once since the objects (as you said it yourself in the comments section) are of the same type.
Also please beware of Reflection when performance is of importance.
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.