Check if 2 objects contain the same data in fields - c#

I have a Pharmacy class which contains lots of properties, and a Pharmacy is declared as unique by having the ID property as a key.
I have code in place which fetches all rows in a table from MySQL back to Pharmacy objects with their properties.
I want compare two List<Pharmacy> objects for the entries in them and check if the same ID exists in both tables, if it doesn't exist then add it to a new List<Pharmacy. If the ID exists in both but the data in the objects differs, then save that object to a new List<Pharmacy aswell.
This is how the class looks like.
public class Pharmacy
{
[Key]
public string Tunniste { get; set; }
public string Lyhenne { get; set; }
public string PitkaNimi { get; set; }
public string YlempiYksikko { get; set; }
public string Hierarkiataso { get; set; }
public string VoimassaoloAlkaa { get; set; }
public string VoimassaoloPaattyy { get; set; }
...
}
It's in Finnish but I hope you can live with that.
Here's how I've tried to check if they're identical.
for (int i = 0; i != pharmacyListFromArchive.Count; i++)
{
if (pharmacyListFromArchive[i].Equals(pharmacyListFromNew[i]))
{
Console.WriteLine("Objects are identical.");
}
else
{
Console.WriteLine("Objects are NOT identical. {0} - {1}", pharmacyListFromArchive[i].Tunniste, pharmacyListFromNew[i].Tunniste);
}
}
But when I run that, none of the objects register as identical even though they are identical in data. How can I work around this?

The standard implementation of Equals checks only for reference equality. What is the default behavior of Equals Method?
You can either override the behavior of Equals. Guidelines for Overriding Equals() and Operator == (C# Programming Guide).
public class Pharmacy {
// fields ...
public override bool Equals(object obj) {
// If parameter is null return false.
if (obj == null) {
return false;
}
// If parameter cannot be cast to Pharmacy return false.
Pharmacy p = obj as Pharmacy;
if ((System.Object)p == null) {
return false;
}
// Return true if the fields match:
return p.Lyhenne == this.Lyhenne &&
p.PitkaNimi == this.PitkaNimi
// && etc...
;
}
public override int GetHashCode() {
return Lyhenne.GetHashCode() ^ PitkaNimi.GetHashCode() /* ^ etc ... */;
}
}
Or you implement a custom IEqualityComparer IEqualityComparer Interface. This might be preferable if your ORM Mapper relies on the default equals (like Entity Framework does).

Related

Alternatives to IEquatable<>

Assume we have a complex structure containing some other complex types, even possibly containing itself e.g.
public class Box {
public string Name { get; set; }
public List<Box> Boxes { get; set; }
public List<Item> Items { get; set; }
public SomeOtherComplexType BlaBla { get; set; }
...
}
And we need to have a possibility to compare these complex types.
Since overriding object.Equals() will be of a poor perfomance when it comes to generic types
I see a lot of usages of IEquatable<>.
The problem is that using IEquatable<> uglifies the code very much mixing the business logic and the technical part of overriding the Equeals() and GetHashCode() methods.
And the more bigger our structure is - the larger overridden methods are.
It looks awful and oblige to do the same for all inner types.
public class Box : IEquatable<Box> {
public string Name { get; set; }
public List<Box> Boxes { get; set; }
public List<Item> Items { get; set; }
public SomeOtherComplexType BlaBla { get; set; }
public bool Equals(Box other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return
(
Name == other.Name||
Name != null &&
Name.Equals(other.Name)
) &&
(
Boxes == other.Boxes ||
Boxes != null &&
Boxes.SequenceEqual(other.Boxes)
) &&
(
Items == other.Items ||
Items != null &&
Items.SequenceEqual(other.Items)
) &&
(
BlaBla == other.BlaBla ||
BlaBla != null &&
BlaBla.SequenceEqual(other.BlaBla)
);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = 14;
if (Name != null)
hashCode = hashCode * 14 + Name.GetHashCode();
if (Boxes != null)
hashCode = hashCode * 14 + Boxes.GetHashCode();
if (Items != null)
hashCode = hashCode * 14 + Items.GetHashCode();
if (BlaBla!= null)
hashCode = hashCode * 14 + BlaBla.GetHashCode();
return hashCode;
}
}
}
Are there any ways of making it less worse or maybe some other alternatives.
Maybe some ways to hide this implementation and make it as much automated as possible?
There are only 4 fields, but what if we are having some complex types with ~30 types in it, with some nested types etc??
Using something like hybrid composite design pattern seems to be even more complex to implement.
First, IEquatable<T> has no impact on performance for reference types. I would drop all your Equals() logic and be done with it. Unless you really do not want equality by reference.
Then, the only sane solution is to introduce a Guid for the object's identity:
public class Box : IEquatable<Box>
{
public Box(Guid id)
{
Id = id;
}
public Guid Id { get; }
public override bool Equals(object obj) => obj is Box box && Equals(box);
public bool Equals(Box other) => Id == other.Id;
public override int GetHashCode() => Id.GetHashCode();
}

Validation strategy

I'm trying to build a series of attribute classes to make it easier for our development team to validate objects. The objects are POCO classes like this.
public class User
{
public string Name { get; set; }
public string Company { get; set; }
}
I want to decorate this model with a custom attribute.
public class User
{
[MustHaveValue]
public string Name { get; set; }
public string Company { get; set; }
}
Then I would create my own class implementing ValidationAttribute, the base class in .NET Framework, which belongs to System.ComponentModel.DataAnnotations.
public class MustHaveValueAttribute : ValidationAttribute
{
.
.
public override IsValid(object value)
{
// validation logic.
}
}
And then I can validate the User model whenever I want by making the set of instances like ValidationContext, List<ValidationResult>.
But in an enterprise environment, problems just can't be solved by a specific class. My validation scenario requires more complex and more flexible ways. Imagine that one of the required validation scenarios would something like this.
public class User
{
public string Name { get; set; }
public string Company { get; set; }
// Check if an item exists in this list.
[MustHaveMoreThanOneItem]
public IList<Client> Clients { get; set; }
}
Then I would need to make another attribute class
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
.
.
public override IsValid(object value)
{
// Let's assume this value is List<Client> for now.
// I know the exact type, so I'm going to cast it to List<Client> without further considerations
List<Client> clients = value as List<Client>;
if(clients.Count > 0) {
return true;
} else {
return false;
}
}
}
But the problem is that there are a lot of other models that have a nested list items. Try to imagine the time when I want to reuse the MustHaveMoreThanOneItem in one of the other models like...
public class Department
{
public string Name { get; set; }
[MustHaveMoreThanOneItem]
public IList<Employee> { get; set; }
}
You already know that it's not going to work because it was strongly typed only for List<Client>. So I decided to use Generic there to solve this problem.
But to my disappointment, the _Attribute interface doesn't support Generic. There's no additional implementation like _Attribute<T> : Attribute and therefore, no ValidationAttribute<T> alas!! I just cannot use Generic here !!
public class Department
{
public string Name { get; set; }
// No way to use this syntax.
[MustHaveMoreThanOneItem<Employee>]
public IList<Employee> { get; set; }
}
So I made a conclusion that Attribute must have been designed for a fixed set of validations like email format, card format, null check, and etc IMAO.
But I still want to use an attribute and give a lot of flexibilities in it to prevent the duplicated, verbose validation codes like this.
if(model.Clients.Count > 0) ...
if(model.Name != null) ...
if(model.Clients.GroupBy(x => x.Country == Country.USA).Count >= 1) ...
if(model.Clients.Where(x => x.CompanyName == Company.Google).ToList().Count > 1 ) ...
.
.
.
I want to pose two questions here.
If Attirbute supports Generic, this problem will be solved?
Is there any way to implement Generic Attribute? in order to use
[MustHaveMoreThanOneItem<Employee>] annotation on a class member?
You can generically check any object that implements IEnumerable like this:
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// omitted null checking
var enumerable = value as IEnumerable;
var enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
{
return false;
}
if (!enumerator.MoveNext())
{
return false;
}
return true;
}
}
C# by definition does not support generic type attributes, although this has been requested actively for a long time:
https://github.com/dotnet/roslyn/issues/953
https://github.com/dotnet/csharplang/issues/124
However, you can still inject a type into a validation attribute via constructor. You then can use reflection or whatever you need to define your custom validation criteria.
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
public Type EnumerableType { get; }
public MustHaveMoreThanOneItemAttribute(Type t)
=> this.EnumerableType = typeof(ICollection<>).MakeGenericType(t);
public override bool IsValid(object value)
{
var count = this.EnumerableType.GetProperty("Count").GetValue(value) as int?;
return (count ?? 0) > 1;
}
}
Now this allows you to use something similar to your goal:
public class Department
{
public string Name { get; set; }
[MustHaveMoreThanOneItem(typeof(Employee))]
public IList<Employee> { get; set; }
}

c# Check if custom class is in list<t>

I have a list of custom class called List<Notifications>.
The class is below:
public class Notification
{
public enum Type {
Promotion,
Other
}
public string ID { get; set; }
public string Headline { get; set; }
public string Detail { get; set; }
public Type NotificationType { get; set; }
}
Before adding an instance of the Notification class to my custom list, I want to check if it is already in the list.
What is the best way to achieve this?
You can use 1.) Contains, but then you have to override Equals (+ GethashCode).
bool contains = list.Contains(someNotificationInstance);
For example:
public class Notification
{
public enum Type {
Promotion,
Other
}
public string ID { get; set; }
public string Headline { get; set; }
public string Detail { get; set; }
public Type NotificationType { get; set; }
public override bool Equals(object obj)
{
return obj is Notification && string.Equals(ID, ((Notification)obj).ID);
}
public override int GetHashCode()
{
return ID == null ? 0 : ID.GetHashCode();
}
}
2.) another option is to provide a custom IEqualityComparer<Notification> for Contains:
public class NotificationComparer : IEqualityComparer<Notification>
{
public bool Equals(Notification x, Notification y)
{
return x.ID == y.ID;
}
public int GetHashCode(Notification obj)
{
return obj.ID == null ? 0 : obj.ID.GetHashCode();
}
}
On this way you don't need to modify the original class. You can use it in this way:
bool contains = list.Contains(someInstance, new NotificationComparer());
3.) Probably the easiest approach is using Enumerable.Any:
bool contains = list.Any(n => someInstance.ID == n.ID);
4.) The most efficient approach is using a set if no duplicates are allowed in the collection anyway. Then you can use the first or second approaches for a HashSet<T>:
var set = new HashSet<Notification>(new NotificationComparer());
set.Add(instance1);
bool contains = !set.Add(instance2);
You can check it with Contains method.
if (!mylist.Select(l => l.ID).Contains(mynewid)) {
var item = new Notifcation();
item.ID = mynewid;
item..... // fill the rest
mylist.Add(item);
}
Maybe a better approch would be use of Dictionary.

How can I use Equals or hashcode in my following program

I want to fetch the details of manager from manager class if at run time I give kinid of employee from the employee class. How can I do this using Equals or Hashcode?
public class employee
{
public string empname { get; set;}
public string location { get; set; }
public int kinid { get; set; }
public double magkin { get; set; }
}
public class manager
{
public string magname { get; set; }
public double magkin { get; set; }
}
Dictionary<employee, manager> relation = new Dictionary<employee, manager>();
I haven't used C# in a long time, but Something like this should work:
kinidFind is the kinid you want to search for.
manager findManager(int kinidFind) {
foreach( KeyValuePair<employee, manager> i in relation) {
if (i.Key.kinid==kinidFind) {
return i.Value;
}
}
}
Assuming that kinid is a unique identifier (you don't care about other fields of employee in identifying), then on employee class you could do the following:
override int GetHashCode()
{
return kinid;
}
override bool Equals(Object obj)
{
if (obj == null) return false;
emploee emp = obj as employee;
if ((System.Object)emp == null) return false;
return (kinid == emp.kinid);
}
However, this is not a good general solution because what if later on you want to find the employee by other fields?
Consider changing that dictionary to:
Dictionary<int,manager> where the int is the kinid of the employee then it's self explanatory.
or since the Dictionary has been loaded you can Enumerate it this way as well it's just a question of feel or taste at this point from Brads answer
foreach (var pair in relation)
{
if (pair.key == kinidFind )
{
return relation[pair.key];
}
}

Dynamic equality checking of multiple properties of a type's objects

I have a type like:
class Order
{
public List<IItem> AllItems { get; set; }
public string Name { get; set; }
public double TotalPurchases { get; set; }
public long Amount { get; set; }
public int Code { get; set; }
}
I've implemented the IEquatable<T> interface to check if two objects of this type are same or not. The current Equals method looks like:
public virtual bool Equals(Order other)
{
if ((object)other == null)
{
return false;
}
return (this.AllItems.Equals(other.AllItems)
&& this.Name.Equals(other.Name)
&& this.TotalPurchases.Equals(other.TotalPurchases)
&& this.Amount.Equals(other.Amount))
&& this.Code.Equals(other.Code));
}
But I wish to implement this method in such a way that it dynamically checks for equality of all the existing properties (or maybe certain properties of this type) without explicitly writing the code for comparison checks as above.
Hope I was able to express my question with clarity. :)
Thanks!
You could write a custom attribute that attaches to the properties on your type which you want to be included in the comparision. Then in the Equals method you could reflect over the type and extract all the properties which have the attribute, and run a comparison on them dynamically.
Psuedo code:
[AttributeUsage(AttributeTarget.Property)]
class IncludeInComparisonAttribute : Attribute { }
class Order
{
List<AllItem> Items { get; set; }
[IncludeInComparison]
string Name { get; set; }
long Amount { get; set; }
[IncludeInComparison]
int Code { get; set; }
override bool Equals(Order other)
{
Type orderType = typeof(Order);
foreach (PropertyInfo property in orderType.GetProperties()
{
if (property.CustomAttributes.Includes(typeof(IncludeInComparisonAttribute))
{
object value1 = property.GetValue(this);
object value2 = propetty.GetValue(other);
if (value1.Equals(value2) == false)
return false;
}
}
return true;
}
}
It'll certianly need to be a bit more elaborate than that, but that should hopefully set you on the right track :)
Two Orders are considered the same if all their properties are equal. It's OK for the 4 properties Name/TotalPurchases/Amount/Code, their default comparers are exactly what you want. But for the property AllItems (whose type is List<IItem>), you must tell how they consider to be equal. Currently you are using reference equals that is incorrect. this.AllItems.Equals(other.AllItems) should be something like:
this.AllItems.SequenceEqual(other.AllItems, new ItemComparer())
And the ItemComparer is a class implements IEqualityComparer<Item> to tell how to check if two Items are equal.

Categories

Resources