Can't remove item from Iesi.Collections.Generic ISet<T> - c#

I have a collection of type:
Iesi.Collections.Generic
public ISet<ItemBinding> ItemBindings { get; set; }
where ItemBinding is Domain.Model
I initialize the collection in this way:
ItemBindings = new HashedSet<ItemBinding>();
and I fill the collection with members.
When i want to remove an item from this collection i can't remove it.
private void OnRemove(ItemBinding itemToRemove) {
ItemBindings.Remove(itemToRemove);
}
even the itemToRemove has the same hashCode as the item from the collection.
Also I tried in to find the item in collection, keep it in a variable, and remove it:
private void OnRemove(ItemBinding itemToRemove) {
var foundItem = ItemBindings.Single( x => x.Id == itemToRemove.Id); // always if found
ItemBindings.Remove(foundItem);
}
but this doesn't work.
An workaround which works ok is this:
private void OnRemove(ItemBinding itemToRemove) {
var backUpItems = new List<ItemBinding>(ItemBindings);
backUpItems.Remove(itemToRemove);
ItemBindings.Clear();
ItemBindings.AddAll(backUpItems);
}
but this is an dirty workaround. I'm trying to do this simple Remove in an elegant manner :).
CHANGE the TYPE
If I change the type from ISet in IList it works ok.
public IList<ItemBinding> ItemBindings { get; set; }
ItemBindings = new List<ItemBinding>();
When i want to remove an item from this collection IT IS REMOVED.
private void OnRemove(ItemBinding itemToRemove) {
ItemBindings.Remove(itemToRemove);
}
What i'm missing in the way that i can't remove items from ISet ... ?
Thank you for suggestions, solutions.

This is very simple problem. Just download dotPeek 1.2 and fire up symbol server & you can then check into the ACTUAL implementation of ISet.Remove() and see why it's being picky. As #HansPassant said, it's probably the case of GetHashCode(), or the actual implementation of HashedSet
As for my speculations; take a look at DictionarySet(base class of HashedSet):
https://www.symbolsource.org/Public/Metadata/Default/Project/NHibernate/3.0.0.Alpha2/Release/All/Iesi.Collections/Iesi.Collections/Generic/DictionarySet.cs
As you can see, Remove() uses Contains() to actually test if it should remove element or not. What does Contains() do? Basically it's wrapper around Dictionary.Contains()
http://msdn.microsoft.com/en-us/library/ms182358(v=vs.80).aspx
GetHashCode returns a value based on the current instance that is
suited for hashing algorithms and data structures such as a hash
table. Two objects that are the same type and are equal must return
the same hash code to ensure that instances of
System.Collections.HashTable and System.Collections.Generic.Dictionary
work correctly.
Notice it's important that:
1) Your GetHashCode() can't change. This means that all the fields which are used by GetHashCode() can't change. As soon as you insert element into Dictionary, its GetHashCode() will be called and it's being put to specific bucket. You can't recover it when you have different GetHashCode() later. After the GetHashCode() has passed, your Equals method will be called. Make sure your fields are immutable.
Relevant source from Dictionary:
private int FindEntry(TKey key)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets != null)
{
int num = this.comparer.GetHashCode(key) & int.MaxValue;
for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
{
if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
return index;
}
}
return -1;
}
See this thread how to override Equals() AND GetHashCode():
Why is it important to override GetHashCode when Equals method is overridden?
Notice the answer by #Albic, in that thread.

I cannot reproduce this behaviour.
private class ItemBinding
{
public string ID { get; set; }
}
[TestMethod]
public void TestMethod1()
{
System.Collections.Generic.HashSet<ItemBinding> set = new System.Collections.Generic.HashSet<ItemBinding>();
ItemBinding item1 = new ItemBinding() { ID = "Jaffa" };
set.Add(item1);
Assert.IsTrue(set.Count == 1);
set.Remove(item1);
Assert.IsTrue(set.Count == 0);
ItemBinding item2 = new ItemBinding() { ID = "Moon" };
set.Add(item2);
ItemBinding item3 = new ItemBinding() { ID = "Moon" };
Assert.IsTrue(item2.GetHashCode() != item3.GetHashCode());
Assert.IsTrue(set.Remove(item3) == false);
Assert.IsTrue(set.Count == 1);
}
The above test shows Hashset working as expected. Is is possible you are falling into the trap shown in the second test of comparing two instances of a class that have the same values, but are in fact different class instances (therefore fail the GetHashCode equality test ?).
If you can alter the posted code here to more accurately represent your particular problem, that would be helpful.

You can try using...
private void OnRemove(ItemBinding itemToRemove)
{
ItemBindings.RemoveWhere(x => x.Id == itemToRemove.Id);
}

Related

remove duplicate from list in object c#

I'm desperately trying to delete all the items with a list of the same value inside.
Here's the code:
private void Button_deleteDouble_MouseDown(object sender, EventArgs e)
{
boardGenerate.Add(new BoardInformation(146, new List<string> { "test" }));
boardGenerate.Add(new BoardInformation(545, new List<string> { "test" }));
boardGenerate = boardGenerate.DistinctBy(x => x.positionQueen).ToList();
}
Normally, since the two lists inside the object are the same, the .DistinctBy() command should remove one of the two objects.
But no, my object list still has the same two objects with the same list
.positionQueen is the name of the variable containing the list
Could somebody help me?
Edit :
The DistinctBy() method comes from MoreLinq.
And this is my BoardInformation class:
public class BoardInformation
{
public BoardInformation(int nbQueen, List<string> positionQueen)
{
this.nbQueen = nbQueen;
this.positionQueen = positionQueen;
}
public int nbQueen { get; set; }
public List<string> positionQueen { get; set; }
}
Set-based operations like Distinct and DistinctBy need a way of determining whether two values are the same. You're using DistinctBy, so you're already asking MoreLINQ to compare the "inner lists" for equality - but you're not saying how to do that.
List<T> doesn't override Equals or GetHashCode, which means it inherits the reference equality behaviour from System.Object. In other words, if you create two separate List<T> objects, they won't compare as equal, even if they have the same content. For example:
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
Console.WriteLine(list1.Equals(list2)); // False
You need to tell DistinctBy how you want to compare the two lists, using an IEqualityComparer<T> - where T in this case is List<string> (because that's the type of BoardInformation.positionQueen.
Here's an example of a generic ListEqualityComparer you could use:
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class ListEqualityComparer<T> : IEqualityComparer<List<T>>
{
private readonly IEqualityComparer<T> elementComparer;
public ListEqualityComparer(IEqualityComparer<T> elementComparer) =>
this.elementComparer = elementComparer;
public ListEqualityComparer() : this(EqualityComparer<T>.Default)
{
}
public bool Equals(List<T> x, List<T> y) =>
ReferenceEquals(x, y) ? true
: x is null || y is null ? false
// Delegate to LINQ's SequenceEqual method
: x.SequenceEqual(y, elementComparer);
public int GetHashCode(List<T> obj)
{
if (obj is null)
{
return 0;
}
// Just a very simple hash implementation
int hash = 23;
foreach (var item in obj)
{
hash = hash * 31 +
(item is null ? 0
: elementComparer.GetHashCode(item));
}
return hash;
}
}
You'd then pass that to DistinctBy, like this:
// We're fine to use the default *element* comparer (string.Equals etc)
var comparer = new ListEqualityComparer<string>();
boardGenerate = boardGenerate.DistinctBy(x => x.positionQueen, comparer).ToList();
Now DistinctBy will call into the comparer, passing in the lists, and will consider your two BoardInformation objects are equal - so only the first will be yielded by DistinctBy, and you'll end up with a list containing a single item.
It comes down to whether a equality check is using referential equality or value equality...you want value equality based on a specific property and that has to be done by hand.
When there is no IEqualityComparer provided which can used to compare individual objects (which is need by the Distinct call), the system determines the equality from each item's references by using their derived object low level service method call of GetHashCode from each reference; hence a reference difference is done and all your values in the list are unique (not equal) regardless of similar property values.
What you are looking for is to have value equality checked specifically for the nbQueenProperty.
To fully utilize Distinct one must create a IEqualityComparer and modify the GetHashCode. By specifing the hash value which can make objects equal...you can weed out the same positionQueen (or other properties) instances out.
Example
public class MyClass
{
public string Name { get; set; }
public int nbQueen { get; set; }
}
Equality comparer to weed out all nbQueen similarities:
class ContactEmailComparer : IEqualityComparer < MyClass >
{
public bool Equals(MyClass x, MyClass y)
{
return x.nbQueen.Equals(y.nbQueen); // Compares by calling each `GetHashCode`
}
public int GetHashCode(MyClass obj)
{
return obj.nbQueen.GetHashCode(); // Add or remove other properties as needed.
}
}
Test code
var original = new List<MyClass>()
{
new MyClass() { nbQueen = 1, Name="Alpha" },
new MyClass() { nbQueen = 1, Name="Omega" },
new MyClass() { nbQueen = 3, Name="Delta" }
};
IEqualityComparer<MyClass> comparer = new ContactEmailComparer();
var newOne = original.Distinct( comparer ).ToList();
Result of the value of newOne :
To be clear...
... .DistinctBy() command should remove one of the two objects.
Does not remove anything. It returns a reference to a new list that should be distinct via the equality operation. The original list (the reference to it) does not change.
LINQ solution
because you have another List inside your class you can not use District or DistrictBy, alternatively, you can use LINQ to filter the list.
boardGenerate = (from b in boardGenerate
from l in b.positionQueen
group new { l,b } by l into g
select g.First().b
).ToList();
// this returns just first duplicate item like district

Why does EF try to insert instance twice with either single or multiple SaveChanges()?

I have the following class structure
class A { long Id { get; set; } ICollection<B> ManyB { get; set; } }
class B { long Id { get; set; } C { get; set; } }
class C { long Id { get; set; } Name { get; set; } }
With an instance graph that looks like this
When I add an instance of A that has in ManyB two different instances of B that reference the same instance of C, EF attempts to insert C twice when I call this code:
var a = CreateItAsDescribedAbove();
context.Add(a)
context.SaveChanges();
All of the objects above are new; that is they are not existing entities already in the database. When I change the code to the following, I still have the same behavior:
var a = CreateItAsDescribedAbove();
A.ManyB
.Select(b => b.C)
.DistinctBy(c => c.Id)
.ForEach(c => context.Add(c));
context.SaveChanges();
context.Add(a)
A.ManyB
.Select(b => b.C)
.ForEach(c => context.Entry(c).State = EntityState.Unchanged);
context.SaveChanges();
I would've thought forcing EF to treat the objects as unchanged after a prior add should work, but they seemed to be ignored.
How do I convince EF to save my instance of C once? Note, I can't simply null out one of the B.C because then one of the relationships won't get inserted. I'm not concerned about doing this in a single commit. Just whatever works.
There is an issue when using object and Equals operator: when on object is used in a hash tables or dictionary keys, is very importat to override GetHashCode method.
EF uses dictionary for internal context.
Try to override GetHashCode for your classes and you must be sure to return same result for same instance object.
EDIT:
EF use internal ObjectStateManager with several dictionary: AddedEntityStore, ModifiedEntityStore, DeletedEntityStore, UnchangedEntityStore (Dictionary<'EntityKey, EntityEntry>).
When EF searchs entities in these dictionary on "SaveChanges", uses TryGetValue method of dictionary.
So, for brevity, this is the code:
private int FindEntry(TKey key)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets != null)
{
int num = this.comparer.GetHashCode(key) & int.MaxValue;
for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
{
if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key))
return index;
}
}
return -1;
}
all of us can see this statement:
this.comparer.GetHashCode(key)
And this is relevant like use of GetHashCode with Dictionary and HashTable.
See Dino Esposito post: http://software2cents.wordpress.com/2014/04/12/hash-codes-and-equality-in-the-net-framework/
Ok, after a very thorough debugging and another set of eyes, the answer is quite simple: they were not the same reference. When I originally posted this I was looking at the wrong property!

How can I get a unique id for a List<T>

I have a List<T>, where T is a class that has an int field for an id. How is the best way to get a unique id value that has not been used in any of the objects in the List<T>?
How is this procedure normally coded? Is there a data type that can help with this or do I need to store the largest id value?
EDIT
How about when I get an id for an object that is 1. I then delete the object from the List. When I create a new object, I would like the unique id to be 2. In this situation, is there any better way than to store the last unique id?
Thanks.
For that approach I'd write an inherited class of List<T> which holds the logic, so you wouldn't need to implement it everywhere you access the list.
You can even leave it generic if you have a minimum interface which has the Id value.
interface IWithId {
int Id { get; set; }
}
class CustomList<T> : List<T> where T : class, IWithId {
private lastUsedId = 1;
public void AddObjectWithAutomaticId(T newObject) {
newObject.Id = lastUsedId++;
base.Add(newObject);
}
public T GetElementById(int id) {
return base.SingleOrDefault(p => p.Id == id);
}
}
The Remove method would still work as before. The class stores the last used Id, independent what you remove. The Add method is also still available, when you want to add an object with a given Id and not auto-fill it.
I agree with the comment that a GUID would suit you as an id property. If, however, you need to use an int then I suggest a new class.
The problem with inheriting List<T> is that you would have to override multiple methods to ensure that things such as Add(), AddRange(), Insert() can't add duplicate ids and update the stored maximum id. It would be easy to miss one.
I would use a class which doesn't inherit anything, but uses a dictionary internally. This won't have all the same methods as a List<T>, but that's not necessarily a bad thing - it saves mistakes being made and you can have a ToList() method for when they want to query it as if it was a List<T>.
Using part of a previous answer to ensure that T has an Id property gives:
interface IHasId {
int Id { get; set; }
}
class AutoIdList<T> where T : class, IHasId {
private readonly IDictionary<int, T> _dictionary = new Dictionary<int, T>();
//Using this list ensures you don't duplicate ids even
//for an item added with an explicit id then removed
private IList<int> _historicalIds = new List<int>();
private int highestAutoGeneratedId = 0;
public List<T> ToList() {
return _dictionary.Values.ToList();
}
public void Add(T item, bool generateId) {
if (generateId) {
highestAutoGeneratedId = NextId();
T.Id = highestAutoGeneratedId;
}
Add(T);
}
public void Replace(T item) {
_dictionary[item.Id] = item;
}
public void Remove(T item) {
_dictionary.Remove(item.Id);
}
private void Add(T item) {
if (_historicalIds.Contains(T.Id)) {
//throw an appropriate exception
} else {
_historicalIds.Add(T.Id);
_dictionary.Add(T.Id, T);
}
}
private int NextId() {
var id = highestAutoGeneratedId + 1;
while (_historicalIds.Contains(id)) {
id++;
}
return id;
}
//More methods to simulate AddRange, Insert, etc if required. Leave if not.
//Should all have simple logic but require checking of Id if adding anything
//Also need logic to maintain the list of used ids
}

CollectionViewSource sort algorithm

How to change the sort algorithm of CollectionViewSource? In fact, i found that the sort algorithm of CollectionViewSource is not stable. And i want to use a stable algorithm on the CollectionViewSource. How can i do that?
I've managed to get a stable sorting using a custom Comparer, but it kinda feels like a big hack...
Just like Benjamin suggested, I get the ListCollectionView from the collection and set its CustomSort property with my custom Comparer. The only difference is that I pass the collection to the Comparer when instantiating it.
private void Sorting(IEnumerable collection)
{
var view = CollectionViewSource.GetDefaultView(collection) as ListCollectionView;
if (view != null)
{
view.CustomSort = new StableComparer(collection);
}
}
Then, in my custom Comparer, I use the collection in the Compare method just to fallback to the items indexes when the comparison returns a zero (they are the same or have the same value).
public class StableComparer : IComparer
{
public IEnumerable Collection { get; set; }
public StableComparer(IEnumerable collection)
{
Collection = collection;
}
public int Compare(object x, object y)
{
IComparable x_Comparable = x as IComparable;
IComparable y_Comparable = y as IComparable;
if (x_Comparable != null && y_Comparable != null)
{
var comparison = x_Comparable.CompareTo(y_Comparable);
// A zero value means x and y are equivalent for sorting, and they could
// be rearranged by an unstable sorting algorithm
if (comparison == 0 && Collection != null)
{
// IndexOf is an extension method for IEnumerable (not included)
var x_Index = Collection.IndexOf(x);
var y_Index = Collection.IndexOf(y);
// By comparing their indexes in the original collection, we get to
// preserve their relative order
if (x_Index != -1 && y_Index != -1)
comparison = x_Index.CompareTo(y_Index);
}
return comparison;
}
return 0;
}
}
I'm still testing this, so I can't guarantee this would work all the time... One problem would be keeping the Collection property inside the Comparer updated, for instance. Or supporting two sort directions.
But I think the idea is clear, though hacky, as I said.
You might want to check out how to implement your custom sorting logic.
In short, set your comparer like this:
private void Sort(object sender, RoutedEventArgs args)
{
BlogPosts posts = (BlogPosts)(this.Resources["posts"]);
ListCollectionView lcv = (ListCollectionView)(CollectionViewSource.GetDefaultView(posts));
lcv.CustomSort = new SortPosts();
}
And implement it like this:
public class SortPosts : IComparer
{
public int Compare(object x, object y)
{
(…)
}
}

Compare generic Collection?

I have a
ObservableCollection<BasicClass> allCollection;
ObservableCollection<BasicClass> selectedCollection;
where
BasicClass
{
public Name {get;set;}
public Age {get;set;}
}
Now I added many BasicClass items to allCollection and only selected BasicClass to selectedCollection
SomeWhere I want to add items in selectedCollection which are not there in allCollection.
I tried this
foreach(var a in allCollection)
{
foreach(var s in selectedCollection)
if(a.Name!=s.Name)
//selectedCollection.Add(new BasicClass {Name =a.Name, Age=a.Age});
}
But the problem is that this code is adding new BasicClass for each and every unmatched name,
but my actuall requirement is, for each Name of allCollection compare all selectedCollection items. If it is not there then add else move for next Item.
LINQ solution could help this? Actually I achieved this by more if and flags but That looks ver hectic.
My traditional solution
foreach(var a in allCollection)
{
bool same = false;
foreach(var s in selectedCollection)
if(a.Name==s.Name)
same=true;
}
if(same==false)
selectedCollection.Add(new BasicClass {Name =a.Name, Age=a.Age});
And I hate this..
EDIT:
I don't want compare collection to collection.
I want to compare collection1 value to collection2 all values, and if it not there then I want to add
Are you sure you don't just need this?
foreach(var a in allCollection)
{
if (!selectedCollection.Contains(a))
selectedCollection.Add(new BasicClass {Name =a.Name, Age=a.Age});
}
EDIT
I've just seen your comment below about matching on name only, so the above is not really what you want:). Try this approach instead:
foreach(var a in allCollection)
{
if (!selectedCollection.Any(s => a.Name == s.Name))
{
selectedCollection.Add(new BasicClass {Name =a.Name, Age=a.Age});
}
}
EDIT
As Chris suggested you could also use "Except" to create a collection. I'm not sure this gains much, it may be quicker but it involves writing the comparer code and creates a new temporary collection. However, it is pretty succinct E.g. Once you had the comparaer written you would just need this to add your missing items to the collection:
selectedCollection.Concat(allCollection.Except(selectedCollection));
So basically you need a 'where-not-in'? Linq->Except is the way to go, to filter on BasicClass.name only implement the IEqualityComparer for Except.
I'm not sure I understood your requirements correctly, so i may be missing the point...
Your BasicClass should implement the IEquatable<BasicClass> interface, so that two instances of BasicClass can be compared for equality:
class BasicClass : IEquatable<BasicClass>
{
public Name {get;set;}
public Age {get;set;}
public bool Equals(BasicClass other)
{
if (other == null)
return false;
return string.Equals(this.Name, other.Name);
}
public override int GetHashCode()
{
return Name == null ? 0 : Name.GetHashCode();
}
}
Now you can use the Except method to find items that are in allCollection but not in selectedCollection:
BasicClass[] notSelected = allCollection.Except(selectedCollection).ToArray();
foreach(BasicClass item in notSelected)
{
selectedCollection.Add(item);
}
Alternatively, you can implement a IEqualityComparer<BasicClass> and pass it to Except (instead of implementing IEquatable<BasicClass> in BasicClass)
You're right, this is more easily accomplished with Linq:
var itemsToAdd = allCollection.Except(selectedCollection);
foreach (var item in itemsToAdd)
selectedCollection.Add(item);
On the other hand, this is just going to make both lists contain the exact same items. Sure this is what you want?
If BasicItem overrides 'Equals' and 'GetHashCode' based off of Name, then this is all you need. If it doesn't, then you will also need to implement an IEqualityComparer:
//Allows us to compare BasicItems as if Name is the key
class NameComparer: IEqualityComparer<BasicItem>
{
public bool Equals(BasicItem first, BasicItem second)
{
return first.Name == second.Name;
}
public int GetHashCode(BasicItem value)
{
return value.Name.GetHashCode;
}
}
You now pass an instance of this class to Except:
var itemsToAdd = allCollections.Except(selectedCollection, new NameComparer());

Categories