How to compare Two Object -Exclude specific Properties in Comparison [closed] - c#

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I have a case there I want to compare two objects in c#.Also I would have option to exclude specific properties when comparing. Can anyone suggest a better approach. Class will look like as below
public class Address
{
public string AddressID { get; set; }
public int AddressStagingID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string County { get; set; }
public string Postcode { get; set; }
public string Country { get; set; }
public bool PreferredAddress { get; set; }
public int? DBID { get; set; }
public Enum AddressStatus Status { get; set; }
}
I need to have a function like
private bool IsAddressModified(Address currentAddress,Address existingAddress)
{
}

Compare NET Objects have everything you need!
Ignore By Expression
CompareLogic compare = new CompareLogic();
compare.Config.IgnoreProperty<Person>(x => x.Name);
Ignore By the ClassName.MemberName
CompareLogic compare = new CompareLogic();
compare.Config.MembersToIgnore.Add("Person.Name");
Ignore All Members with a Name
CompareLogic compare = new CompareLogic();
compare.Config.MembersToIgnore.Add("UpdateDate");
Ignore with a Wildcard
CompareLogic compare = new CompareLogic();
compare.Config.MembersToIgnore.Add("*Id");
Ignore with an Attribute
[AttributeUsage(AttributeTargets.Property)]
public sealed class CompareIgnoreAttribute : Attribute
{
}
public class Shipment
{
public long IdentCode { get; set; }
public String Customer { get; set; }
[CompareIgnore]
public DateTime InsertDate { get; set; }
}
CompareLogic compare = new CompareLogic();
compare.Config.AttributesToIgnore.Add(typeof(CompareIgnoreAttribute));
Compare Only Properties with an Attribute
public class Movie
{
[Compare]
public string Name { get; set; }
public decimal PaymentForTomCruise { get; set; }
}
CompareLogic compare = new CompareLogic();
compare.Config.RequiredAttributesToCompare.Add(typeof(CompareAttribute));

I tried to develop a different solution using Expression Trees which is, in my opinion, more flexible
public class Test
{
public static void Main()
{
Address a1 = new Address();
a1.AddressID = "100";
Address a2 = new Address();
a2.AddressID = "200";
Console.WriteLine(IsAddressModified(a1,a2,a=>a.AddressID));
}
public static bool IsAddressModified(Address a1,Address a2,params Expression<Func<Address,Object>>[] props)
{
if(props == null)
return a1.Equals(a2);
foreach(Expression<Func<Address,object>> memberExpression in props)
{
MemberExpression property = memberExpression.Body as MemberExpression;
if(property != null)
{
foreach(PropertyInfo pi in typeof(Address).GetProperties())
{
// exclude all properties we passed in
if(!pi.Name.Equals(property.Member.Name))
{
var valueA1 = pi.GetValue(a1);
var valueA2 = pi.GetValue(a2);
if(valueA1 != null && valueA2 != null)
if(!valueA1.Equals(valueA2))
return true;
}
}
}
}
return false;
}
}
So what does the code?
You can pass an array of 'properties' to the method IsAddressModified. These properties will be excluded while comparing.
From the Expression I extract a MemberExpression to get the Name of each Property.
I iterate through all Properties the type Address has and check if it is one property to exclude.
Last but not least, I compare the property values.
Why so 'complicated' ?
With this solution you can pass as much properties into the function as you like AND you are totally type-safe during compilation.
In the Main you can see how I call this function. Even due to the fact that AddressID of a1 and a2 differ, the function returns false, because you excluded the AddressID.
A full compilable example can be found here

How about Reflection:
private bool IsAddressModified(Address currentAddress, Address existingAddress)
{
foreach (PropertyInfo pi in currentAddress.GetType().GetProperties())
{
//To exclude properties use condition
if (pi.Name != "City") {
object currentElement = typeof(Address).GetProperty(pi.Name).GetValue(currentAddress,null);
object existingElement = typeof(Address).GetProperty(pi.Name).GetValue(existingAddress,null);
if (!currentElement.Equals(existingElement))
{ return false; }
}
return true;
}
}

If you are looking for something very simple, use reflection. But if you need something advanced, use CompareObjects. Here is the Nuget. This library can give detailed reports on changes as well. Which means you can use it for logging etc.
This is a sample code from the site.
//This is the comparison class
CompareLogic compareLogic = new CompareLogic();
//Create a couple objects to compare
Person person1 = new Person();
person1.DateCreated = DateTime.Now;
person1.Name = "Greg";
Person person2 = new Person();
person2.Name = "John";
person2.DateCreated = person1.DateCreated;
ComparisonResult result = compareLogic.Compare(person1, person2);
//These will be different, write out the differences
if (!result.AreEqual)
Console.WriteLine(result.DifferencesString);

Brute force?
private bool IsAddressModified(Address a, Address b)
{
return a.Address1 != b.Address1 || a.Address2 != b.Address2
|| a.City != b.City || a.PostCode != b.PostCode;
// etc. for all the properties that are considered as modified
}

Related

Assignment same property values to other

I have different classes sharing some properties of same type and name. I wish to assign same property values to each other. I explain my intention better in comments in the following pseudo-code. Is it possible in C#?
Ponder that there are a plethora of common properties but in unrelated classes, must we assign them one-by-one?
Second case is about sharing same properties but some of them may be nullable, who knows!
Side note: the classes already exist, cannot be altered, touched. Kinda sealed.
Can't it be done using nameofoperator and two for loops? Compare property names if matched, assign?
using System;
namespace MainProgram
{
class HomeFood
{
public DateTime Date { get; set; }
public string food1 { get; set; }
public string food2 { get; set; }
public int cucumberSize { get; set; }
}
class AuntFood
{
public string food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime Date { get; set; }
public string food1 { get; set; }
// extra
public double? length { get; set; }
}
class GrandpaFood
{
public string? food2 { get; set; }
public int cucumberSize { get; set; }
public DateTime? Date { get; set; }
public string food1 { get; set; }
// extra
}
static class Program
{
public static void Main(string[] args)
{
var home = new HomeFood
{
Date = new DateTime(2020, 1, 1),
food1 = "cucumber",
food2 = "tomato",
cucumberSize = 123
};
var aunt = new AuntFood();
/*
First case: same types
Expected for-each loop
assigning a class's property values
to other class's property values
or for-loop no matter
foreach(var property in HomeFood's properties)
assign property's value to AuntFood's same property
*/
var home2 = new HomeFood();
var grandpa = new GrandpaFood
{
Date = new DateTime(2020, 1, 1),
food1 = "dfgf",
food2 = "dfgdgfdg",
cucumberSize = 43534
};
/*
Second case: similar to first case
with the exception of same type but nullable
or for-loop no matter
foreach(var property in GrandpaFood's properties)
assign property's value to GrandpaFood's same property
we don't care if it is null e.g.
Home2's same property = property's value ?? default;
*/
}
}
}
Based on the comments in the questions, this is just to show how it can be done with reflection.
Disclaimer, this is just a very simplified example on how to use reflection to sync properties. It does not handle any special cases (modifiers, read only, type mismatch, etc)
I would strongly suggest to use automapper to achieve the qp goals.
public class Type1
{
public string Property1 { get; set; }
public string Property2 { get; set; }
}
public class Type2
{
public string Property1 { get; set; }
public string Property3 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var t1 = new Type1 { Property1 = "Banana" };
var t2 = new Type2();
var properties1 = typeof(Type1).GetProperties().ToList();
var properties2 = typeof(Type2).GetProperties().ToList();
foreach(var p in properties1)
{
var found = properties2.FirstOrDefault(i => i.Name == p.Name);
if(found != null)
{
found.SetValue(t2, p.GetValue(t1));
}
}
Console.WriteLine(t2.Property1);
}
}
The short answer is, apply OOP. Define a base Food class and inherit from it in any specific food classes you have. You can put all the shared props in the base class.
public class Food
{
public string food2 { get; set; }
// other shared stuff
}
class GrandpaFood : Food
{
// other specific stuff
}
As others have said, use some of the Object Oriented properties, like inheriting a super class of implement an interface.
In case you go for inheritance, consider making the super class (the one you inherit from) abstract. This means that the super class itself cannot be instantiated, which greatly reduces the risk of violating the Liskov Substitutional Principle. Also it often reflects the real problem better. In your example, this would also be the case, as “food” is not an actual thing in the real world, but rather a group of things.

Find in List<object> where each item is in an array

I'm making a web service call and getting data back that I am adding to a list. No problems doing that. My remedyinfo list has content that I can verify with break points in VS.
I cannot seem to figure out how to search the list for a value that matches a new variable, for instance I want to find if incidentID is equal to "INC000000001".
I've tried var foundItem = remedyinfo.Contains("searchvalue") but it always returns false.
I've tried the LINQ queries as suggested in other post:
var foundItem = myArray.SingleOrDefault(item => item.intProperty == someValue);
What I do notice is that the sample refers to comparing item.intProperty == somevalue) should work, I am not able to get any reference after item., only suggested Equals, GetType, GetHashCode and ToString. So, I cannot reference item.incidentID for example.
Any guidance is greatly appreciated.
var remedyinfo = new List<object> { };
remedyinfo.Add(new IncidentItem()
{
assignedgroup = assignedgroup,
incidentID = incidentID,
submitdate = offsetDate.ToString(),
priority = priority,
status = status,
assignee = assignee,
summarydesc = summarydesc,
notes = notes
});
[Serializable]
public class IncidentItem
{
public string assignedgroup { get; set; }
public string incidentID { get; set; }
public string submitdate { get; set; }
public string priority { get; set; }
public string status { get; set; }
public string assignee { get; set; }
public string summarydesc { get; set; }
public string notes { get; set; }
}
To check if list contains an item, there are different options. You can use Any in .Net 3.5 or higher:
if (remedyinfo.Any(incident => incident.incidentID == "Hello"))
// rest of code
If you have another IncidentItem and wish to check in list like
var anotherIncident = new IncidentItem();
if (remedyInfo.Contains(anotherIncident))
then you either have to implement IEquatable or override Equals and HashCode in IncidentItem class.
More on Equals and HashCode is here:
Correct way to override Equals() and GetHashCode()

Using reflection to obtain list of DisplayNames from Class properties [duplicate]

This question already has answers here:
get the value of DisplayName attribute
(18 answers)
Closed 4 years ago.
I'm trying to obtain a List of DisplayNames out of a class that has most of its properties booleans:
public class AccessoriesModel
{
public int Id { get; set; }
[Display(Name = "Acc 1")]
public bool Accessory1 { get; set; }
[Display(Name = "Acc 2")]
public bool Accessory2 { get; set; }
[Display(Name = "Acc 3")]
public bool Accessory3 { get; set; }
[Display(Name = "Acc 4")]
public bool Accessory4 { get; set; }
}
by iterating over the PropertyInfos of the class and seeing which values are true, as below:
List<string> list = new List<string>();
foreach (PropertyInfo propertyInfo in data.GetType().GetProperties())
{
if (propertyInfo.PropertyType == typeof(bool))
{
bool value = (bool)propertyInfo.GetValue(data, null);
if (value)
{
//add the DisplayName of the item who's value is true to the list named "list"
//the following line works fine, but I cannot iterate over the list of items to get dinamicaly build the list
string displayName = GetPropertyDisplayName<AccessoriesModel>(i => i.AirConditioning);
list.add(displayName)
}
}
}
where GetPropertyDisplayName is a solution suggested by a fellow member in his answer for another question for retrieving the DisplayName of a property: https://stackoverflow.com/a/10048758
The end result that I'm looking for is a list of strings (display names) that will be formed only by the properties that are true.
Thank you, in advance, for your help on this.
I think you are using the wrong attribute. I just took a snippet from https://stackoverflow.com/a/5015878/6866739 and replaced "DisplayNameAttribute" with "DisplayAttribute" and I got working results.
The sample code you referred to has its properties like:
public class Class1
{
[DisplayName("Something To Name")]
public virtual string Name { get; set; }
Yours are like:
public class AccessoriesModel
{
public int Id { get; set; }
[Display(Name = "Acc 1")]
public bool Accessory1 { get; set; }
So that difference in the usage of attributes, might've been the reason why it wasn't working for you. Rest, you can find the working code below:
foreach (PropertyInfo propertyInfo in data.GetType().GetProperties())
{
if (propertyInfo.PropertyType == typeof(bool))
{
bool value = (bool)propertyInfo.GetValue(data, null);
if (value)
{
var attribute = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true)
.Cast<DisplayAttribute>().Single();
string displayName = attribute.Name;
list.Add(displayName);
}
}
}
I reused the extension methods from this answer https://stackoverflow.com/a/5015911/6866739

ValueInjecter Null Values

I'am using ValueInjector(3.x) over AutoMapper but I have some questions.
First, I don't understand the difference between UnflatLoopInjection and FlatLoopInjection.
Also i want to set values in complex types.
Class Product {
public string Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
Class ProductDTO {
public string Name { get; set; }
public Category Category { get; set; }
}
var product = repository.Get(id);
product.InjectFrom(dto);
The problem is my product.Category already have some properties with values and using InjectFrom the value injector replace the product.Category to dto.Category replacing the entire category even replacing to null.
Thanks
flattening is when you go from
Foo1.Foo2.Foo1.Name to Foo1Foo2Foo1Name
unflattening the other way around
I understand that you want to avoid injecting when the source property is Null
for this you can create an injections like this:
public class AvoidNullProps : LoopInjection
{
protected override void SetValue(object source, object target, PropertyInfo sp, PropertyInfo tp)
{
var val = sp.GetValue(source);
if(val != null)
tp.SetValue(target, val);
}
}
and use it
res.InjectFrom<AvoidNullProps>(src);
you could also use the Mapper:
Mapper.AddMap<ProductDTO, Product>(dto =>
{
var res = new Product();
res.Id = dto.Id;
res.Name = dto.Name;
if(dto.Category != null && dto.Category.Id != null)
res.Category = Mapper.Map<Category>(dto.Category);
return res;
});
var product = Mapper.Map<Product>(dto);

C# - copying property values from one instance to another, different classes

I have two C# classes that have many of the same properties (by name and type). I want to be able to copy all non-null values from an instance of Defect into an instance of DefectViewModel. I was hoping to do it with reflection, using GetType().GetProperties(). I tried the following:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
PropertyInfo[] defectProperties = defect.GetType().GetProperties();
IEnumerable<string> viewModelPropertyNames =
defectViewModel.GetType().GetProperties().Select(property => property.Name);
IEnumerable<PropertyInfo> propertiesToCopy =
defectProperties.Where(defectProperty =>
viewModelPropertyNames.Contains(defectProperty.Name)
);
foreach (PropertyInfo defectProperty in propertiesToCopy)
{
var defectValue = defectProperty.GetValue(defect, null) as string;
if (null == defectValue)
{
continue;
}
// "System.Reflection.TargetException: Object does not match target type":
defectProperty.SetValue(viewModel, defectValue, null);
}
What would be the best way to do this? Should I maintain separate lists of Defect properties and DefectViewModel properties so that I can do viewModelProperty.SetValue(viewModel, defectValue, null)?
Edit: thanks to both Jordão's and Dave's answers, I chose AutoMapper. DefectViewModel is in a WPF application, so I added the following App constructor:
public App()
{
Mapper.CreateMap<Defect, DefectViewModel>()
.ForMember("PropertyOnlyInViewModel", options => options.Ignore())
.ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore())
.ForAllMembers(memberConfigExpr =>
memberConfigExpr.Condition(resContext =>
resContext.SourceType.Equals(typeof(string)) &&
!resContext.IsSourceValueNull
)
);
}
Then, instead of all that PropertyInfo business, I just have the following line:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);
Take a look at AutoMapper.
There are frameworks for this, the one I know of is Automapper:
http://automapper.codeplex.com/
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
Replace your erroneous line with this:
PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name);
targetProperty.SetValue(viewModel, defectValue, null);
Your posted code is attempting to set a Defect-tied property on a DefectViewModel object.
In terms of organizing the code, if you don't want an external library like AutoMapper, you can use a mixin-like scheme to separate the code out like this:
class Program {
static void Main(string[] args) {
var d = new Defect() { Category = "bug", Status = "open" };
var m = new DefectViewModel();
m.CopyPropertiesFrom(d);
Console.WriteLine("{0}, {1}", m.Category, m.Status);
}
}
// compositions
class Defect : MPropertyGettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
class DefectViewModel : MPropertySettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
// quasi-mixins
public interface MPropertyEnumerable { }
public static class PropertyEnumerable {
public static IEnumerable<string> GetProperties(this MPropertyEnumerable self) {
return self.GetType().GetProperties().Select(property => property.Name);
}
}
public interface MPropertyGettable : MPropertyEnumerable { }
public static class PropertyGettable {
public static object GetValue(this MPropertyGettable self, string name) {
return self.GetType().GetProperty(name).GetValue(self, null);
}
}
public interface MPropertySettable : MPropertyEnumerable { }
public static class PropertySettable {
public static void SetValue<T>(this MPropertySettable self, string name, T value) {
self.GetType().GetProperty(name).SetValue(self, value, null);
}
public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) {
self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach(
property => self.SetValue(property, other.GetValue(property)));
}
}
This way, all the code to achieve the property-copying is separate from the classes that use it. You just need to reference the mixins in their interface list.
Note that this is not as robust or flexible as AutoMapper, because you might want to copy properties with different names or just some sub-set of the properties. Or it might downright fail if the properties don't provide the necessary getters or setters or their types differ. But, it still might be enough for your purposes.
This is cheap and easy. It makes use of System.Web.Script.Serialization and some extention methods for ease of use:
public static class JSONExts
{
public static string ToJSON(this object o)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Serialize(o);
}
public static List<T> FromJSONToListOf<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<List<T>>(jsonString);
}
public static T FromJSONTo<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<T>(jsonString);
}
public static T1 ConvertViaJSON<T1>(this object o)
{
return o.ToJSON().FromJSONTo<T1>();
}
}
Here's some similiar but different classes:
public class Member
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string PetName { get; set; }
public int PetAge { get; set; }
public bool IsUgly { get; set; }
}
public class MemberV2
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string ChildName { get; set; }
public int ChildAge { get; set; }
public bool IsCute { get; set; }
}
And here's the methods in action:
var memberClass1Obj = new Member {
Name = "Steve Smith",
Age = 25,
IsCitizen = true,
Birthday = DateTime.Now.AddYears(-30),
PetName = "Rosco",
PetAge = 4,
IsUgly = true,
};
string br = "<br /><br />";
Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON
var memberClass2Obj = memberClass1Obj.ConvertViaJSON<MemberV2>();
Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled
For one thing I would not place that code (somewhere) external but in the constructor of the ViewModel:
class DefectViewModel
{
public DefectViewModel(Defect source) { ... }
}
And if this is the only class (or one of a few) I would not automate it further but write out the property assignments. Automating it looks nice but there may be more exceptions and special cases than you expect.
Any chance you could have both classes implement an interface that defines the shared properties?

Categories

Resources