i have trouble in adding duplicate element to a list
i want to add that object:
public class Allegato : BaseObject<Allegato, int>
{
public override int Id { get; set; }
public virtual string NomeFile { get; set; }
}
in BaseObject i implement equals looking only at Id Field
i cannot change this settings since those is need to my NHibernate Data Access Infrastracture
now i have other class with a list of Allegato objects
public class Corso : BaseObject<Corso, int>
{
public virtual ICollection<Allegato> Allegati { get; set; } = new List<Allegato>();
public virtual void AddAllegato(Allegato allegato)
{
this.Allegati.Add(allegato);
}
}
Now i need to add many Allegato to collection and then save it to database, ID will be empty since will be generate by DB sequence
using (myUow = myUowFactory.Create())
{
var obj = new Corso();
//populate corso
myUow.Corsi.Create(obj);
var files = SessionManagement.LeggiOggetto<SessionObject.File>(HttpContext, SessionManagement.ChiaveSessione.File);
foreach (var file in files)
obj.AddAllegato(new Allegato { NomeFile = file.Nome });
myUow.SaveAll();
}
first object is added but all other no. first element remain all others are not added
debugging it see that equals method of Allegato class is called, how can i avoid it?
thanks
EDIT
base object class
public abstract class BaseObject<TEntity, TKey> : EquatableObject<TEntity>
where TEntity : class
{
public abstract TKey Id { get; set; }
public override int GetHashCode()
{
return Id.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;
return BaseObjectEquals(other);
}
public override bool Equals(TEntity obj)
{
if (obj == null)
return false;
BaseObject<TEntity, TKey> other = obj as BaseObject<TEntity, TKey>;
return BaseObjectEquals(other);
}
public virtual bool BaseObjectEquals(BaseObject<TEntity, TKey> other)
{
if (other == null)
return false;
return EqualityComparer<TKey>.Default.Equals(this.Id , other.Id);
}
private IEnumerable<FieldInfo> GetFields()
{
Type t = GetType();
List<FieldInfo> fields = new List<FieldInfo>();
while (t != typeof(object))
{
fields.AddRange(t.GetTypeInfo().DeclaredFields.Where(x => x.FieldType.Name != typeof(ICollection<>).Name));//.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public));
t = t.GetTypeInfo().BaseType;
}
return fields;
}
public static bool operator ==(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(x, y))
{
return true;
}
// If one is null, but not both, return false.
if (((object)x == null) || ((object)y == null))
{
return false;
}
return x.Equals(y);
}
public static bool operator !=(BaseObject<TEntity, TKey> x, BaseObject<TEntity, TKey> y)
{
return !(x == y);
}
}
as #Panagiotis Kanavos stated, problem is in NHibernate overwrite of my property with a Set collection. I changed the map to Bag to make it work
Related
There is a class of Products:
public class ProductWithFeatures
{
public string Name { get; set; }
public ICollection<Feature> Features { get; set; }
}
public class Feature
{
public int Id { get; set; }
public Feature(int Id)
{
this.Id = Id;
}
}
I want to write an IEqualityComparer for this (i already have one for Feature).
The one for Feature is like this:
public class FeatureComparer : IEqualityComparer<Feature>
{
public bool Equals(Feature x, Feature y)
{
return x.Id == y.Id;
}
public int GetHashCode(Feature obj)
{
return obj.Id;
}
}
And what i wrote so far on the other one is this:
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && LinqHomework.FeatureComparer.Equals(x.Features, y.Features);
}
public int GetHashCode(ProductWithFeatures obj)
{
}
}
I can't find an answer anywhere about this. Does anybody know how to write it?
Two ProductWithFeaturess are equal if they have the same name, and have the same features in the same order.
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features.SequenceEqual(y.Features, new LinqHomework.FeatureComparer());
}
public int GetHashCode(ProductWithFeatures obj)
{
int hash = obj.Name.GetHashCode();
var featureComparer = new LinqHomework.FeatureComparer();
foreach (var feature in obj.Features)
{
hash = hash * 23 + featureComparer.GetHashCode(feature);
}
return hash;
}
}
This is a simple approach, which can be improved in a number of ways.
First, let's give our FeatureComparer a Default property, so we don't need to keep creating new instances:
public class FeatureComparer : IEqualityComparer<Feature>
{
public static FeatureComparer Default { get; } = new FeatureComparer();
// ... as before
}
This lets us write:
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features.SequenceEqual(y.Features, LinqHomework.FeatureComparer.Default);
}
public int GetHashCode(ProductWithFeatures obj)
{
int hash = obj.Name.GetHashCode();
foreach (var feature in obj.Features)
{
hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
}
return hash;
}
}
We're also not handling the case where our methods are passed null, or the name of a feature is null, so let's deal with those. We can also test whether x and y are the same object in Equals.
We'll also do the integer operations in an unchecked block in case it overflows (and the assembly is compiled with /checked).
Note that we use ReferenceEquals instead of ==, in case you end up implementing the == operator in your types.
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
if (x.Name != y.Name)
return false;
if (ReferenceEquals(x.Features, y.Features))
return true;
if (ReferenceEquals(x.Features, null) || ReferenceEquals(y.Features, null))
return false;
if (!x.Features.SequenceEquals(y.Features, LinqHomework.FeatureComparer.Default))
return false;
return true;
}
public int GetHashCode(ProductWithFeatures obj)
{
if (ReferenceEquals(obj, null))
return 0;
unchecked
{
int hash = obj.Name?.GetHashCode() ?? 0;
if (!ReferenceEquals(obj.Features, null))
{
foreach (var feature in obj.Features)
{
hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
}
return hash;
}
}
}
}
It's really up to you. I personally would go for something like
public int GetHashCode( ProductWithFeatures obj )
{
string toHash = obj.Name;
foreach( var feature in obj.Features )
toHash += feature.GetHashCode();
return toHash.GetHashCode();
}
It's not the nicest code ever, but it does what it's supposed to do.
I am using this answer Difference between two lists to find a difference between two lists using Linq Except. However, my objects in the lists are of generic type, so I wrote a Comparer for the generic types, then I pass it to Except method. However I receive the following error message:
Error CS1929 'List' does not contain a definition for 'Except' and the best extension method overload 'ParallelEnumerable.Except(ParallelQuery, ParallelQuery, IEqualityComparer)' requires a receiver of type 'ParallelQuery'
My call:
var differenceList = list1.Except(list2, new PropertyComparer<Guid>("ObjectId"));
And my comparer:
public class PropertyComparer<T> : IEqualityComparer<T>
{
private PropertyInfo _PropertyInfo;
public PropertyComparer(string propertyName)
{
_PropertyInfo = typeof(T).GetProperty(propertyName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
if (_PropertyInfo == null)
{
throw new ArgumentException(
string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
}
}
public int GetHashCode(T obj)
{
object propertyValue = _PropertyInfo.GetValue(obj, null);
if (obj == null) return 0;
return propertyValue.GetHashCode();
}
public bool Equals(T x, T y)
{
object xValue = _PropertyInfo.GetValue(x, null);
object yValue = _PropertyInfo.GetValue(y, null);
if (xValue == null)
{
return yValue == null;
}
return xValue.Equals(yValue);
}
}
And from the declaration of Except method in System.Linq, which fits my comparer:
public static IEnumerable<TSource> Except<TSource>(
this IEnumerable<TSource> first,
IEnumerable<TSource> second,
IEqualityComparer<TSource> comparer
);
What am I doing wrong and how can I get it working?
UPDATE
list1 and list2 types:
List<Contact>
Where Contact is
public class Contact : EntityBase
{
public Guid ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Email> Emails {get; set;}
}
EntityBase is
public class EntityBase : IEntity
{
public IQueryable<T> GetDBEntities<T>(ApplicationDbContext db) where T : class
{
return db.Set<T>();
}
public List<T> GetLocalEntities<T>() where T : class
{
var localProperties = this.GetType().GetProperties();
foreach (var localProperty in localProperties)
{
var localPropertyValue = localProperty.GetValue(this);
if (localPropertyValue != null && localPropertyValue.IsGenericList() == true)
{
var localPropertyValueType = localPropertyValue.GetType(); // List<object>
var localPropertyValueTypeDecoupled = localPropertyValueType.GetGenericArguments().Single(); // List<T>
if (localPropertyValueTypeDecoupled == typeof(T))
{
return (List<T>)localPropertyValue;
}
}
}
throw new Exception("Entity Types Validation Error");
}
public void ProcessEntityReference<T>(ApplicationDbContext db) where T : class
{
// T is Email
var remoteList = this.GetDBEntities<T>(db).ToList();
var updatedList = GetLocalEntities<T>();
var toBeAdded = updatedList.Except(remoteList, new PropertyComparer<Guid>("ContactId"));
var toBeDeleted = new List<object>();
throw new NotImplementedException();
}
public void ProcessEntityReferences(ApplicationDbContext db)
{
this.ProcessEntityReference<Email>(db);
}
}
You should pass the same type used by your list to the Except method, in your example, you are using Guid but it should be of type Contact, also, your Contact class doesn't have a property called "ObjectId", try changing that for "ContactId", the following seems to work fine:
static void Main(string[] args)
{
var list1 = new List<Contact>();
list1.Add(new Contact() { ContactId = Guid.Parse("FB58F102-0CE4-4914-ABFF-ABBD3895D719") });
list1.Add(new Contact() { ContactId = Guid.Parse("5A201238-6036-4385-B848-DEE598A3520C") });
var list2 = new List<Contact>();
list2.Add(new Contact() { ContactId = Guid.Parse("FB58F102-0CE4-4914-ABFF-ABBD3895D719") });
var list3 = list1.Except(list2, new PropertyComparer<Contact>("ContactId"));
foreach (var item in list3)
Console.WriteLine(item.ContactId.ToString());
Console.ReadLine();
}
public class Contact
{
public Guid ContactId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class PropertyComparer<T> : IEqualityComparer<T>
{
private PropertyInfo _PropertyInfo;
public PropertyComparer(string propertyName)
{
_PropertyInfo = typeof(T).GetProperty(propertyName,
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public);
if (_PropertyInfo == null)
{
throw new ArgumentException(
string.Format("{0} is not a property of type {1}.", propertyName, typeof(T)));
}
}
public int GetHashCode(T obj)
{
object propertyValue = _PropertyInfo.GetValue(obj, null);
if (obj == null) return 0;
return propertyValue.GetHashCode();
}
public bool Equals(T x, T y)
{
object xValue = _PropertyInfo.GetValue(x, null);
object yValue = _PropertyInfo.GetValue(y, null);
if (xValue == null)
{
return yValue == null;
}
return xValue.Equals(yValue);
}
}
Output:
5a201238-6036-4385-b848-dee598a3520c
I have following class.
public class Header
{
public long CombinedIndex { get; private set; }
public int Key { get; set; } //Unique
public bool IsRequired { get; set; }// True or false
public DateTime? AvailableDate { get; set; } // null or date value
public int Index { get; set; } // a number and can be same among other Header
public void CalculateCombinedIndex()
{
CombinedIndex = Key + (IsRequired ? 0 : 1) + Index + (AvailableDate ?? DateTime.MaxValue).Ticks;
}
}
I am expected around 50,000 instances
of it in an array. I need to sort them in following order:
First based on IsRequired
Then Index
Then Available date (date value near to current date)
Key (primary key)
Does it make sense to say sum all these property value and
derive a unique number and then only sort that number.
Later, the list of Header can be converted to dictionary where
Key is CombinedIndex and my already sorted array can lookup that
dictionary in order I iterated the array. How efficient and
promising this all looks?
I would do it with LINQ:
var ordered = list
.OrderByDescending(x => x.IsRequired)
.ThenBy(x => x.Index)
.ThenBy(x=> x.AvailableDate)
.ThenBy(x=> x.Key);
Only in case of ties the next condition will be used. So no need to create a combined index which makes it very difficult to change the order logic and which seems to be broken anyway.
If you need to perform comparison operations based on a projected key from a given type, I find it helps to have a KeyComparer class that can create an IComparer<TSource> instance from a Func<TSource, TKey> delegate. This works especially well with the new tuple syntax introduced in C# 7. Here's an example of the resulting syntax:
// Create an IComparer<Header> instance based on your combined key.
var comparer = KeyComparer.Create((Header h) => (h.IsRequired, h.Index, h.AvailableDate, h.Key));
List<Header> headers = ...
// Sort the list using the combined key.
headers.Sort(comparer);
// Convert to a dictionary keyed by the combined key.
var dict = headers.ToDictionary(comparer.KeySelector);
And here's a sample implementation of the class. (For a more detailed explanation, see my blog post.)
public static class KeyComparer
{
public static KeyComparer<TSource, TKey> Create<TSource, TKey>(
Func<TSource, TKey> keySelector,
IComparer<TKey> innerComparer = null)
{
return new KeyComparer<TSource, TKey>(keySelector, innerComparer);
}
}
public class KeyComparer<TSource, TKey> : Comparer<TSource>
{
protected internal KeyComparer(
Func<TSource, TKey> keySelector,
IComparer<TKey> innerComparer = null)
{
KeySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector));
InnerComparer = innerComparer ?? Comparer<TKey>.Default;
}
public Func<TSource, TKey> KeySelector { get; }
public IComparer<TKey> InnerComparer { get; }
public override int Compare(TSource x, TSource y)
{
if (object.ReferenceEquals(x, y))
return 0;
if (x == null)
return -1;
if (y == null)
return 1;
TKey xKey = KeySelector(x);
TKey yKey = KeySelector(y);
return InnerComparer.Compare(xKey, yKey);
}
}
Just implement IComparable and you can use List.Sort().
public class Header : IComparable
{
public long CombinedIndex { get; private set; }
public int Key { get; set; } //Unique
public bool IsRequired { get; set; }// True or false
public DateTime? AvailableDate { get; set; } // null or date value
public int Index { get; set; } // a number and can be same among other Header
public void CalculateCombinedIndex()
{
CombinedIndex = Key + (IsRequired ? 0 : 1) + Index + (AvailableDate ?? DateTime.MaxValue).Ticks;
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
Header otherHeader = obj as Header;
if (otherHeader != null)
{
if (this.IsRequired && !otherHeader.IsRequired)
return 1;
if (!this.IsRequired && otherHeader.IsRequired)
return -1;
if (this.Index > otherHeader.Index)
return 1;
if (this.Index < otherHeader.Index)
return -1;
//// ....
return 0;
}
else
throw new ArgumentException("Object is not a Temperature");
}
}
i have this certain Key in my Dictionary:
public class KKey
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>();
problem was everytime I generate new Guid, this condition wouldnt work:
if (false == myDictionary.TryGetValue(key, out keyobj))
because Guid key was new ...
Now my question was, how can i make condition that verify if Kkey.Name was already added, then dont add?
You either need to create a custom comparer or have your class override Equals and GetHashCode
Option 1: Comparer
sealed class NameEqualityComparer : IEqualityComparer<KKey>
{
public bool Equals(KKey x, KKey y)
{
return string.Equals(x.Name, y.Name);
}
public int GetHashCode(KKey obj)
{
return (obj.Name != null ? obj.Name.GetHashCode() : 0);
}
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>(new NameEqualityComparer());
Option 2: Override
public class KKey : IEquatable<KKey>
{
public string Name { get; set; }
public Guid Guid { get; set; }
public bool Equals(KKey other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name);
}
public override bool Equals(object obj)
{
return Equals(obj as KKey);
}
public override int GetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>();
can also be
bool alreadyAdded = keywordList.Any(n => n.Key.Name == name);
if (!alreadyAdded) { }
This is generic class to compare two objects with a user defined property, but it doesn't work correctly.
public class ObjectComparer<T> : IEqualityComparer<T>
{
private string _propertyName;
private string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
PropertyInfo = typeof(T).GetProperty(PropertyName);
}
}
public ObjectComparer(string propertyName)
{
PropertyName = propertyName;
}
private PropertyInfo PropertyInfo { get; set; }
public int GetHashCode(T type)
{
if (type== null)
{
return 0;
}
return PropertyInfo.GetHashCode();
}
public bool Equals(T obj1, T obj2)
{
if (ReferenceEquals(obj1, obj2))
{
return true;
}
if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null))
{
return false;
}
return PropertyInfo.GetValue(obj1, null) == PropertyInfo.GetValue(obj2, null);
}
}
Issue 1)type == null (Possible compare of value type with null)
Issue 2)Use ReferenceEquals in this position is correct?
Update
"Word" class:
public class Word
{
public string EnglishText { get; set; }
public string LocalText { get; set; }
public string EditedText { get; set; }
}
Usage :
var except = Program.Localizer.DefaultLanguage.Words.Except(CurrentSelectedLanguage.Words, new ObjectComparer<Word>("EnglishText"));
Number of different objects based on the "EnglishName" is not correct.
Class authentic and Modified below.
Special thanks to Sriram Sakthivel
public class ObjectComparer<T> : IEqualityComparer<T>
{
private string _propertyName;
private string PropertyName
{
get { return _propertyName; }
set
{
_propertyName = value;
PropertyInfo = typeof(T).GetProperty(PropertyName);
}
}
public ObjectComparer(string propertyName)
{
PropertyName = propertyName;
}
private PropertyInfo PropertyInfo { get; set; }
public int GetHashCode(T obj)
{
if (ReferenceEquals(obj, null))
{
return 0;
}
return PropertyInfo.GetHashCode();
}
public bool Equals(T obj1, T obj2)
{
if (ReferenceEquals(obj1, obj2))
{
return true;
}
if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null))
{
return false;
}
return Equals(PropertyInfo.GetValue(obj1, null),
PropertyInfo.GetValue(obj2, null));
}
}
Your GetHashCode is wrong. You're calling PropertyInfo.GetHashCode It should be using the Property value instead.
public int GetHashCode(T obj)
{
if (object.ReferenceEquals(obj, null))
{
return 0;
}
var value = PropertyInfo.GetValue(obj);
return value == null ? 0 : value.GetHashCode();
}
Also you can get rid of Possible compare of value type with null warning from resharper using object.ReferenceEquals
GetHashCode parameter is named as type, which is misleading, So I renamed it to obj
Update:
If you need to use value comparison you must use Object.Equals method as opposed to using == operator for object type
public bool Equals(T obj1, T obj2)
{
if (ReferenceEquals(obj1, obj2))
{
return true;
}
if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null))
{
return false;
}
return object.Equals(PropertyInfo.GetValue(obj1, null),
PropertyInfo.GetValue(obj2, null));
}