Linq to SQL: Unable to Successively insert 2 objects that compare equal - c#

I have a table Size, and I have overloaded GetHashCode and Equals on the corresponding object generated by Linq to SQL.
I am executing the following code:
Size s = new Size();
data_context.Sizes.InsertOnSubmit(s);
data_context.SubmitChanges();
s = new Size
{
Diameter=1
};
data_context.Sizes.InsertOnSubmit(s);
data_context.SubmitChanges();
s = new Size
{
Diameter=1
};
data_context.Sizes.InsertOnSubmit(s);
data_context.SubmitChanges();
On the third SubmitChanges, I get an InvalidOperationException with the message
"Cannot add an entity that already exists."
If I rerun the program I can add the first two again but not the third. I have no clue what's going on, could someone give me a pointer?
If I only override Size.Equals or Size.GetHashCode, this problem doesn't arise, but it does when I override both.
The Equals and GetHashCode override are as follow (but really any kind of method that implement value semantics lead to the same behavior):
public override bool Equals(Object obj)
{
if (obj == null)
{
return false;
}
Size p = obj as Size;
if ((System.Object)p == null)
{
return false;
}
return p.Diameter == Diameter;
}
public override int GetHashCode()
{
return Diameter?.GetHashCode()??0;
}

You said:
I have overloaded GetHashCode and Equals on the corresponding object generated by Linq to SQL.
If you overloaded those so that two Size entities with the same properties are considered equal, then this is working as expected; LINQ to SQL uses equality checks to determine if two objects refer to the same record to keep things in sync.
Why did you override Equals and GetHashCode in the first place? Simply removing those overrides will avoid this issue if you're wanting to be able to insert similar Size objects.

Related

Remove one list from another mvc

I have two lists of the same type and I am trying to subtract the information in one list from the other and then save the result into the model.
I have tried two ways of doing it and so far I can't get either to work:
These are the two lists:
List<ApplicationsDetailsModel> AppList = ctx.Database.SqlQuery<ApplicationsDetailsModel>("exec get_applications_r").ToList();
var AppExceptionList = new List<ApplicationsDetailsModel>();
foreach(var g in AnIrrelevantList)
{
AppExceptionList.Add(new ApplicationsDetailsModel()
{
AppNum = g.AppNum,
AppName = g.AppName
});
}
So they now both have different data in the same format.
model.AppList = AppList.Except(AppExceptionList).ToList();
This doesn't bring up any errors but it also doesn't subtract the second list from the first.
var onlyInFirst = AppList.RemoveAll(a => AppExceptionList.Any(b => AppList == AppExceptionList));
I got this idea from this question.
Anyone know where I am going wrong?
The instances are not the same and are therefore not found to be equal by Except since it's checking for reference equal (which is obviously never going to be the case). For your situation you need to write a custom equality comparer... I've taken a stab at it here...
public class ApplicationsDetailsModelEqualityComparer : IEqualityComparer<ApplicationsDetailsModel>
{
public bool Equals(ApplicationsDetailsModel x, ApplicationsDetailsModel y)
{
return x.AppNum == y.AppNum && x.AppName == y.AppName;
}
public int GetHashCode(ApplicationsDetailsModel obj)
{
int hashCode = (obj.AppName != null ? obj.AppName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ obj.AppNum.GetHashCode();
return hashCode;
}
}
Usage...
model.AppList = AppList.Except(AppExceptionList, new ApplicationsDetailsModelEqualityComparer()).ToList();
Note that I'm assuming your AppNum and AppName together uniquely identify your objects in your list.
The Except method doesn't know how to compare two objects of type ApplicationsDetailsModel. You need to tell him explicitly, using an IEqualityComparer :
public class ApplicationsDetailsModelComparer : IEqualityComparer<ApplicationsDetailsModel> {
public bool Equals(ApplicationsDetailsModel first, ApplicationsDetailsModel second) {
return first.AppNum == second.AppNum;
}
public int GetHashCode(ApplicationsDetailsModel applicationsDetailsModel) {
return applicationsDetailsModel.AppNum.GetHashCode();
}
}
Then, you use it like this :
model.AppList = AppList.Except(AppExceptionList, new ApplicationsDetailsModelComparer ()).ToList();
If AppNum isn't an unique value in your collection (like a primary key), feel free to adapt the comparer class to your needs.
jgauffin's answer on the question that you linked to sums it up:
http://stackoverflow.com/a/13361682/89092
Except requires that Equals and GetHashCode is implemented in the traversed class.
The problem is that the Except method does not now how to compare instances of ApplicationsDetailsModel
You should implement GetHashCode in ApplicationsDetailsModel to create a way to uniquely identify an instance
You should implement Equals in ApplicationsDetailsModel and use the result of GetHashCode to return whether or no the instances should be considered "Equal". It is probably best to do this by implementing the IEquatable interface: http://msdn.microsoft.com/en-us/library/ms131187(v=vs.110).aspx
When you perform these steps, the Except method will work as expected

Retrieved Dictionary Key Not Found

I have a SortedDictionary declared like such:
SortedDictionary<MyObject,IMyInterface> dict = new SortedDictionary<MyObject,IMyInterface>();
When its populated with values, if I grab any key from the dictionary and then try to reference it immediately after, I get a KeyNotFoundException:
MyObject myObj = dict.Keys.First();
var value = dict[myObj]; // This line throws a KeyNotFoundException
As I hover over the dictionary (after the error) with the debugger, I can clearly see the same key that I tried to reference is in fact contained in the dictionary. I'm populating the dictionary using a ReadOnlyCollection of MyObjects. Perhaps something odd is happening there? I tried overriding the == operator and Equals methods to get the explicit comparison I wanted, but no such luck. That really shouldn't matter since I'm actually getting a key directly from the Dictionary then querying the Dictionary using that same key. I can't figure out what's causing this. Has anyone ever seen this behavior?
EDIT 1
In overriding Equals I also overloaded (as MS recommends) GetHashCode as well. Here's the implementation of MyObject for anyone interested:
public class MyObject
{
public string UserName { get; set;}
public UInt64 UserID { get; set;}
public override bool Equals(object obj)
{
if (obj == null || GetType()!= obj.GetType())
{
return false;
}
// Return true if the fields match:
return this.Equals((MyObject)obj);
}
public bool Equals(MyObject other)
{
// Return true if the fields match
return this.UserID == other.UserID;
}
public override int GetHashCode()
{
return (int)this.UserID;
}
public static bool operator ==( MyObject a, MyObject b)
{
// If both are null, or both are same instance, return true.
if (System.Object.ReferenceEquals(a, b))
{
return true;
}
// If one is null, but not both, return false.
if (((object)a == null) || ((object)b == null))
{
return false;
}
// Return true if the fields match:
return a.UserID == b.UserID
}
public static bool operator !=( MyObject a, MyObject b)
{
return !(a == b);
}
}
What I noticed from debugging is that if I add a quick watch (after the KeyNotFoundException is thrown) for the expression:
dict.ElementAt(0).Key == value;
it returns true. How can this be?
EDIT 2
So the problem ended up being because SortedDictionary (and Dictionary as well) are not thread-safe. There was a background thread that was performing some operations on the dictionary which seem to be triggering a resort of the collection (adding items to the collection would do this). At the same time, when the dictionary iterated through the values to find my key, the collection was being changed and it was not finding my key even though it was there.
Sorry for all of you who asked for code on this one, I'm currently debugging an application that I inherited and I didn't realize this was going on on a timed, background thread. As such, I thought I copied and pasted all the relevant code, but I didn't realize there was another thread running behind everything manipulating the collection.
It appears that the problem ended up being because SortedDictionary is not thread-safe. There was a background thread that was performing some operations on the dictionary (adding items to the collection) which seems to be triggering a resort of the collection. At the same time, when the dictionary was attempting to iterate through the values to find my key, the collection was being changed and resorted, rendering the enumerator invalid, and it was not finding my key even though it was there.
I have a suspicion - it's possible that you're changing the UserID of the key after insertion. For example, this would demonstrate the problem:
var key = new MyObject { UserId = 10 };
var dictionary = new Dictionary<MyObject, string>();
dictionary[key] = "foo";
key.UserId = 20; // This will change the hash code
var value = dict[key]; // Bang!
You shouldn't change properties involved in equality/hash-code considerations for an object which is being used as the key in a hash-based collection. Ideally, change your code so that this can't be changed - make UserId readonly, initialized on construction.
The above definitely would cause a problem - but it's possible that it's not the same as the problem you're seeing, of course.
In addition to overloading == and Equals, make sure you override GetHashCode with a suitable hash function. In particular, see this specification from the documentation:
If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not
compare as equal, the GetHashCode methods for the two objects do not
have to return different values.
The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state
that determines the return value of the object's Equals method. Note
that this is true only for the current execution of an application,
and that a different hash code can be returned if the application is
run again.
For the best performance, a hash function should generate an even distribution for all input, including input that is heavily clustered.
An implication is that small modifications to object state should
result in large modifications to the resulting hash code for best hash
table performance.
Hash functions should be inexpensive to compute.
The GetHashCode method should not throw exceptions.
I agree with Jon Skeet's suspicion that you're somehow unintentionally modifying UserID property after it's added as a key. But since the only property that's important for testing equality in MyObject is UserID (and therefore that's the only property that the Dictionary cares about), I'd recommend refactoring your code to use a simple Dictionary<ulong, IMyInterface> instead:
Dictionary<ulong, IMyInterface> dict = new Dictionary<string, IMyInterface>();
ulong userID = dict.Keys.First();
var value = dict[userID];

LINQ - Distinct is ignored?

So I have a problem with my LINQ code, where I have to select a Distinct data set, I implement the following IEqualityComparer:
public class ProjectRoleComparer : IEqualityComparer<ProjectUserRoleMap>
{
public bool Equals(ProjectUserRoleMap x, ProjectUserRoleMap y)
{
return x.RoleID.Equals(y.RoleID);
}
public int GetHashCode(ProjectUserRoleMap obj)
{
return obj.GetHashCode();
}
}
In this context, I wish to retrieve a bunch of ProjectUserRoleMap objects related to a given Project, identified by it's ID, I only want one ProjectUserRoleMap per unique RoleID, but my strict instruction to perform a distinct select on the RoleID is ignored. I am totally clueless as to why this is the case, and do not understand LINQ enough to think of a workaround. Here is the calling code:
ProjectRoleComparer prCom = new ProjectRoleComparer();
IEnumerable<ProjectUserRoleMap> roleList = ProjectData.AllProjectUserRoleMap.Where(x => x.ProjectID == id).Distinct(prCom);
This code gives me 6 entries, when the number of entries I know I want is just 4. Am I doing something wrong with my usage of LINQ?
For reference, the ProjectUserRoleMap object has a RoleID, (int)
Your implementation of GetHashCode is wrong. Return obj.RoleID.GetHashCode();
Background:
Code that consumes an IEqualityComparer<T> usually first compares the hash codes of two objects. Only if those hash codes are the same Equals is called.
It is implemented like this, because two unequal objects can have the same hash key, but two equal objects never can have different hash keys - if GetHashCode() is implemented correctly.
This knowledge is used to improve the efficiency and performance of the comparison as implementations of GetHashCode are supposed to be fast, cheap operations.
Try:
public int GetHashCode(ProjectUserRoleMap obj)
{
return obj.RoleID.GetHashCode();
}

Why ReferenceEquals and == operator behave different from Equals

I have an Entity that doesn't override any of the equality members\operators.
When comparing two proxies of them (I got them from the Nhibernate session) the result changes according to the equality method:
ReferenceEquals(first, second) - false.
first == second - false
Equals(first, second) - true.
This is even more weird as they both exist in the same session context and according to the Nhibernate docs:
NHibernate only guarantees identity ( a == b , the default
implementation of Equals()) inside a single ISession!`
And:
The instance is currently associated with a persistence context. It
has a persistent identity (primary key value) and, perhaps, a
corresponding row in the database. For a particular persistence
context, NHibernate guarantees that persistent identity is equivalent
to CLR identity (in-memory location of the object).
So why not all of the equality methods return true?
Update:
I get the enteties this way, Query the session for ChildEntity and get the Parents Entities with Linq's select, similar to this:
var childs = session.Query<Child>();
var parents = childs.Select(x => x.ParentEntity).ToList();
Edit
You might be using a struct? See below
I suppose reference types show the behaviour you expect:
public class Program {
class X { int x,y; }
public static void Main(string[] args)
{
X a = new X();
X b = new X();
System.Console.WriteLine(a == b);
System.Console.WriteLine(a.Equals(b));
System.Console.WriteLine(Equals(a,b));
System.Console.WriteLine(ReferenceEquals(a,b));
} }
Prints:
False
False
False
False
For structs, things are different (commeting out the a==b test, which doesn't compile for structs:)
public class Program {
struct X { int x,y; }
public static void Main(string[] args)
{
X a = new X();
X b = new X();
//System.Console.WriteLine(a == b);
System.Console.WriteLine(a.Equals(b));
System.Console.WriteLine(Equals(a,b));
System.Console.WriteLine(ReferenceEquals(a,b));
} }
Output:
True
True
False
Rationale:
The default implementation of Equals() comes from class ValueType, which is implicit base class of all value types. You may override this implementation by defining your own Equals() method in your struct. ValueType.Equals() always returns false when one compares objects of different (dynamic) types. If objects are of the same type, it compares them by calling Equals() for each field. If any of these returns false, the whole process is stopped, and final result is false. If all field-by-field comparisons return true, final result is true
If ReferenceEquals returns false, you are clearly comparing two different instances.
Equals might still be true if it's overridden, but I don't think that's where the actual problem is.
I'd like to know how you're mapping and getting those objects, because as the docs say, you can never get two different objects of the same type representing the same row in the same session.
After I get childs from the session I Merge them with the session.
var childs = session.Query<Child>();
// Do some stuff
foreach (var child in childs)
{
session.Merge(child);
}
var parents = childs.Select(x => x.ParentEntity).ToList();
It appear that the merge detached the entity from the session and return a new Proxy attached to the session.
It can be fixed with
var newChild = (Child)session.Merge(child);
// Or:
session.Update(child); // (We have session.Clear() in our tests so I can't use this because it makes troubles when you update detached Entity

Should .GetHashCode() return same value for two objects having different refences in the memory?

I need to override Equals() method for one of my types but it seems I have to also override GetHashCode() method.
I am not sure:
If I have type Animal and if I have 2 instances of Animal which are basically the same(equal)Cats; like:
Animal cat_01 = new Animal("Kitty", "Pink");
Animal cat_02 = new Animal("Kitty", "Pink");
Should I implement the GetHashedCode() to retirn same value for both cas_01 and cat_02 eventhough they represent different references in the memory?
Is it the way GetHashCode() shuold work?
Thanks
MSDN says:
If two objects compare as equal, the GetHashCode method for each
object must return the same value.
So yes, GetHashCode should return the same value for both instances.
You can still use Object.ReferenceEquals if you want to see if they refer to the same object.
I would disagree with the other answers.. Animal in this example is not a value object, it's perfectly feasable that two cats could have the same name & colour and be completely distinct entities. Logically you're saying "this cat and that cat have the same name and the same colour, therefore they are the same cat" - which is not necessarily true..
What I would suggest you do is leave Animal.Equals to the default implementation, and create a seperate IEqualityComparer implementation that returns true if the animals have the same name/colour.
public class AnimalNameColorComparer : IEqualityComparer<Animal>
{
public bool Equals(Animal a, Animal b)
{
return a.Name == b.Name &&
a.Color == b.Color
}
public int GetHashCode(Animal a)
{
return a.Name.GetHashCode() ^ a.Color.GetHashCode();
}
}
Try to remember that there are many different ways to compare a cat, and one single "Equals" method is not enough :)
// Create two black cats called fluffy...
var cat1 = new Cat("Fluffy", "Black");
var cat2 = new Cat("Fluffy", "Black");
cat1.Equals(cat2) == false; // they are not the same cat!
var comparer = new AnimalNameColorComparer();
comparer.Equals(cat1, cat2) == true; // But they do have the same name & colour...
Depending on the design of the model, if it is value object (immutable) then the gethashcode should return a hashed value of all the fields, but on the other hand if it is a domain entity then it should have an identity and this identity should be used in comparison and gethashcode (two persons with the same name and age are not the same, if you have two cats with the same name that does not mean they are the same cat!).
check: http://moh-abed.com/2011/07/13/entities-and-value-objects/

Categories

Resources