Skip to content

Commit e519e2f

Browse files
kriebbhazzik
authored andcommitted
Fix NH-3221
1 parent 4af22ca commit e519e2f

File tree

9 files changed

+413
-7
lines changed

9 files changed

+413
-7
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
namespace NHibernate.Test.NHSpecificTest.NH3221
2+
{
3+
/// <summary>
4+
/// This is a trivial class that is used to make sure that Equals and GetHashCode
5+
/// are properly overloaded with the correct semantics. This is exteremely important
6+
/// if you are going to deal with objects outside the current Unit of Work.
7+
/// </summary>
8+
/// <typeparam name="T"></typeparam>
9+
/// <typeparam name="TKey"></typeparam>
10+
public abstract class EqualityAndHashCodeProvider<T, TKey>
11+
where T : EqualityAndHashCodeProvider<T, TKey>
12+
{
13+
private int? oldHashCode;
14+
15+
/// <summary>
16+
/// Determines whether the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>.
17+
/// </summary>
18+
/// <param name="obj">The <see cref="T:System.Object"></see> to compare with the current <see cref="T:System.Object"></see>.</param>
19+
/// <returns>
20+
/// true if the specified <see cref="T:System.Object"></see> is equal to the current <see cref="T:System.Object"></see>; otherwise, false.
21+
/// </returns>
22+
public override bool Equals(object obj)
23+
{
24+
T other = obj as T;
25+
if (other == null)
26+
return false;
27+
//to handle the case of comparing two new objects
28+
bool otherIsTransient = Equals(other.Id, default(TKey));
29+
bool thisIsTransient = Equals(this.Id, default(TKey));
30+
if (otherIsTransient && thisIsTransient)
31+
return ReferenceEquals(other, this);
32+
return other.Id.Equals(Id);
33+
}
34+
35+
/// <summary>
36+
/// Serves as a hash function for a particular type. <see cref="M:System.Object.GetHashCode"></see> is suitable for use in hashing algorithms and data structures like a hash table.
37+
/// </summary>
38+
/// <returns>
39+
/// A hash code for the current <see cref="T:System.Object"></see>.
40+
/// </returns>
41+
public override int GetHashCode()
42+
{
43+
//This is done se we won't change the hash code
44+
if (oldHashCode.HasValue)
45+
return oldHashCode.Value;
46+
bool thisIsTransient = Equals(this.Id, default(TKey));
47+
//When we are transient, we use the base GetHashCode()
48+
//and remember it, so an instance can't change its hash code.
49+
if (thisIsTransient)
50+
{
51+
oldHashCode = base.GetHashCode();
52+
return oldHashCode.Value;
53+
}
54+
return Id.GetHashCode();
55+
}
56+
57+
/// <summary>
58+
/// Get or set the Id of this entity
59+
/// </summary>
60+
public abstract TKey Id { get; protected set; }
61+
62+
/// <summary>
63+
/// Equality operator so we can have == semantics
64+
/// </summary>
65+
public static bool operator ==(EqualityAndHashCodeProvider<T, TKey> x, EqualityAndHashCodeProvider<T, TKey> y)
66+
{
67+
return Equals(x, y);
68+
}
69+
70+
/// <summary>
71+
/// Inequality operator so we can have != semantics
72+
/// </summary>
73+
public static bool operator !=(EqualityAndHashCodeProvider<T, TKey> x, EqualityAndHashCodeProvider<T, TKey> y)
74+
{
75+
return !(x == y);
76+
}
77+
}
78+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
using System;
2+
using System.Linq;
3+
using NUnit.Framework;
4+
5+
namespace NHibernate.Test.NHSpecificTest.NH3221
6+
{
7+
[TestFixture]
8+
public class WeirdBehaviour : BugTestCase
9+
{
10+
private Guid nicePersonId;
11+
12+
[Test]
13+
public void CanAddATodo()
14+
{
15+
using (ISession session = OpenSession())
16+
{
17+
var person = new Person("myName");
18+
person.AddTodo(new Todo(person) { Name = "I need to get it" });
19+
session.Save(person);
20+
nicePersonId = person.Id;
21+
Assert.AreEqual(1, person.Todos.Count());
22+
session.Flush();
23+
}
24+
using (ISession session = OpenSession())
25+
{
26+
var person = session.Get<Person>(nicePersonId);
27+
Assert.AreEqual(1, person.Todos.Count());
28+
Assert.AreEqual(person.Todos.ToList()[0].Person.Id, person.Id);
29+
}
30+
}
31+
32+
[Test]
33+
public void CanRemoveATodo()
34+
{
35+
Todo myTodo;
36+
using (ISession session = OpenSession())
37+
{
38+
var person = new Person("myName2");
39+
myTodo = person.AddTodo(new Todo(person) { Name = "I need to get it" });
40+
session.Save(person);
41+
nicePersonId = person.Id;
42+
Assert.AreEqual(1, person.Todos.Count());
43+
session.Flush();
44+
}
45+
46+
using (ISession session = OpenSession())
47+
{
48+
var person = session.Get<Person>(nicePersonId);
49+
Assert.AreEqual(1, person.Todos.Count());
50+
person.RemoveTodo(myTodo);
51+
Assert.AreEqual(0, person.Todos.Count());
52+
session.Flush();
53+
}
54+
55+
using (ISession session = OpenSession())
56+
{
57+
var person = session.Get<Person>(nicePersonId);
58+
Assert.AreEqual(0, person.Todos.Count());
59+
}
60+
}
61+
62+
[Test]
63+
public void CanAddStuff()
64+
{
65+
using (ISession session = OpenSession())
66+
{
67+
var person = new Person("myName3");
68+
person.AddStuff(new Stuff(person) { Name = "this pen is mine" });
69+
session.Save(person);
70+
nicePersonId = person.Id;
71+
Assert.AreEqual(1, person.MyStuff.Count());
72+
session.Flush();
73+
}
74+
75+
using (ISession session = OpenSession())
76+
{
77+
var person = session.Get<Person>(nicePersonId);
78+
Assert.AreEqual(1, person.MyStuff.Count());
79+
Assert.AreEqual(person.MyStuff.ToList()[0].Person.Id, person.Id);
80+
}
81+
}
82+
83+
[Test]
84+
public void CanRemoveStuff()
85+
{
86+
Stuff myStuff;
87+
using (ISession session = OpenSession())
88+
{
89+
var person = new Person("MyName4");
90+
myStuff = person.AddStuff(new Stuff(person) { Name = "BallPen" });
91+
session.Save(person);
92+
nicePersonId = person.Id;
93+
Assert.AreEqual(1, person.MyStuff.Count());
94+
session.Flush();
95+
}
96+
97+
using (ISession session = OpenSession())
98+
{
99+
var person = session.Get<Person>(nicePersonId);
100+
Assert.AreEqual(1, person.MyStuff.Count());
101+
person.RemoveStuff(myStuff);
102+
Assert.AreEqual(0, person.MyStuff.Count());
103+
session.Flush();
104+
}
105+
106+
using (ISession session = OpenSession())
107+
{
108+
var person = session.Get<Person>(nicePersonId);
109+
Assert.AreEqual(0, person.MyStuff.Count());
110+
}
111+
}
112+
113+
protected override void OnSetUp()
114+
{
115+
base.OnSetUp();
116+
Console.WriteLine("=====================BEGIN TEST");
117+
}
118+
119+
protected override void OnTearDown()
120+
{
121+
Console.WriteLine("=====================END TEST");
122+
base.OnTearDown();
123+
using (ISession session = OpenSession())
124+
{
125+
const string hql = "from System.Object";
126+
session.Delete(hql);
127+
session.Flush();
128+
}
129+
}
130+
}
131+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test"
3+
namespace="NHibernate.Test.NHSpecificTest.NH3221"
4+
>
5+
<class name="Person" table="Persons" >
6+
<id name="Id" column="id" type="guid" >
7+
<generator class="guid.comb" />
8+
</id>
9+
<natural-id mutable="true" >
10+
<property name="Name" length="25" type="string" column="name" lazy="false" />
11+
</natural-id>
12+
13+
<property name="NickName" length="25" type="string" column="nickname" access="property" lazy="false" />
14+
15+
<set name="Todos" access="nosetter.camelcase" lazy="extra" cascade="all-delete-orphan" table="Todos" >
16+
<key foreign-key="Person_Todo" column="personId" />
17+
<composite-element class="Todo">
18+
<property column="name" name="Name" type="string"/>
19+
<many-to-one name="Person" formula="personId" class ="Person" access="property" update="false" insert="false" />
20+
</composite-element>
21+
</set>
22+
<bag name="MyStuff" access="nosetter.camelcase" lazy="false" cascade="all-delete-orphan" table="Stuff" >
23+
<key foreign-key="Person_Stuff" column="personId" />
24+
<composite-element class="Stuff">
25+
<property column="name" name="Name" type="string"/>
26+
<many-to-one name="Person" formula="personId" class ="Person" access="property" update="false" insert="false" />
27+
</composite-element>
28+
</bag>
29+
</class>
30+
31+
</hibernate-mapping>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Iesi.Collections.Generic;
4+
5+
namespace NHibernate.Test.NHSpecificTest.NH3221
6+
{
7+
public class Person : EqualityAndHashCodeProvider<Person, Guid>
8+
{
9+
private readonly ISet<Todo> todos;
10+
11+
private readonly IList<Stuff> myStuff;
12+
13+
public override Guid Id { get; protected set; }
14+
15+
public virtual string Name { get; set; }
16+
17+
public virtual string NickName { get; set; }
18+
19+
public virtual Person Partner { get; set; }
20+
21+
public virtual IEnumerable<Todo> Todos { get { return todos; } }
22+
23+
public virtual IEnumerable<Stuff> MyStuff { get { return myStuff; } }
24+
25+
protected Person()
26+
{
27+
todos = new HashedSet<Todo>();
28+
myStuff = new List<Stuff>();
29+
}
30+
31+
public Person(string name):this()
32+
{
33+
Name = name;
34+
}
35+
36+
public virtual Todo AddTodo(Todo todo)
37+
{
38+
todos.Add(todo);
39+
return todo;
40+
}
41+
42+
public virtual Stuff AddStuff(Stuff stuff)
43+
{
44+
myStuff.Add(stuff);
45+
return stuff;
46+
}
47+
48+
public virtual void RemoveTodo(Todo myTodo)
49+
{
50+
todos.Remove(myTodo);
51+
}
52+
53+
public virtual void RemoveStuff(Stuff stuff)
54+
{
55+
myStuff.Remove(stuff);
56+
}
57+
}
58+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3221
4+
{
5+
public class Stuff : IEquatable<Stuff>
6+
{
7+
protected Stuff() { }
8+
9+
public Stuff(Person personNeedsTodo)
10+
{
11+
Person = personNeedsTodo;
12+
}
13+
14+
public virtual string Name { get; set; }
15+
16+
public virtual bool Equals(Stuff other)
17+
{
18+
if (other == null) return false;
19+
return Name.Equals(other.Name) && Person.Equals(other.Person);
20+
}
21+
22+
public static bool operator ==(Stuff left, Stuff right)
23+
{
24+
return Equals(left, right);
25+
}
26+
27+
public static bool operator !=(Stuff left, Stuff right)
28+
{
29+
return !Equals(left, right);
30+
}
31+
32+
public override bool Equals(object obj)
33+
{
34+
if (ReferenceEquals(null, obj)) return false;
35+
if (ReferenceEquals(this, obj)) return true;
36+
if (obj.GetType() != typeof(Stuff)) return false;
37+
return Equals((Stuff)obj);
38+
}
39+
40+
public override int GetHashCode()
41+
{
42+
return (Name != null ? Name.GetHashCode() : 0);
43+
}
44+
45+
public virtual Person Person { get; protected set; }
46+
}
47+
}

0 commit comments

Comments
 (0)