|
From: <jul...@us...> - 2010-09-01 17:08:18
|
Revision: 5173
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5173&view=rev
Author: julian-maughan
Date: 2010-09-01 17:08:11 +0000 (Wed, 01 Sep 2010)
Log Message:
-----------
Fixes incorrect tracking of identifiers when performing add/insert/remove operations on PersistentIdentifierBag collection. Thanks go to Patrick Earl for this one (ref. NH2279, NH2111)
Modified Paths:
--------------
trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericIdentifierBag.cs
trunk/nhibernate/src/NHibernate/Collection/PersistentIdentifierBag.cs
trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
Added Paths:
-----------
trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/
trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/B.cs
trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/C.cs
trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Fixture.cs
trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Mappings.hbm.xml
Modified: trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericIdentifierBag.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericIdentifierBag.cs 2010-08-31 18:08:13 UTC (rev 5172)
+++ trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericIdentifierBag.cs 2010-09-01 17:08:11 UTC (rev 5173)
@@ -78,7 +78,7 @@
void IList<T>.Insert(int index, T item)
{
Write();
- BeforeAdd(index);
+ BeforeInsert(index);
gvalues.Insert(index, item);
}
Modified: trunk/nhibernate/src/NHibernate/Collection/PersistentIdentifierBag.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Collection/PersistentIdentifierBag.cs 2010-08-31 18:08:13 UTC (rev 5172)
+++ trunk/nhibernate/src/NHibernate/Collection/PersistentIdentifierBag.cs 2010-09-01 17:08:11 UTC (rev 5173)
@@ -77,7 +77,7 @@
private object GetIdentifier(int index)
{
// NH specific : To emulate IDictionary behavior but using Dictionary<int, object> (without boxing/unboxing for index)
- object result;
+ object result = null;
identifiers.TryGetValue(index, out result);
return result;
}
@@ -217,16 +217,14 @@
return old != null && elemType.IsDirty(old, entry, Session);
}
- public override object ReadFrom(IDataReader reader, ICollectionPersister persister, ICollectionAliases descriptor,
- object owner)
+ public override object ReadFrom(IDataReader reader, ICollectionPersister persister, ICollectionAliases descriptor, object owner)
{
object element = persister.ReadElement(reader, owner, descriptor.SuffixedElementAliases, Session);
- object tempObject = GetIdentifier(values.Count);
- identifiers[values.Count] = persister.ReadIdentifier(reader, descriptor.SuffixedIdentifierAlias, Session);
- object old = tempObject;
- if (old == null)
+ object id = persister.ReadIdentifier(reader, descriptor.SuffixedIdentifierAlias, Session);
+ if (!identifiers.ContainsValue(id))
{
- values.Add(element); //maintain correct duplication if loaded in a cartesian product
+ identifiers[values.Count] = id;
+ values.Add(element);
}
return element;
}
@@ -290,11 +288,6 @@
protected void BeforeRemove(int index)
{
- if (!identifiers.ContainsKey(index))
- return; // index not previously persisted, nothing to do
-
- // Move the identifier being removed to the end of the list (i.e. it isn't actually removed).
- object removedId = identifiers[index];
int last = values.Count - 1;
for (int i = index; i < last; i++)
{
@@ -308,14 +301,22 @@
identifiers[i] = id;
}
}
- identifiers[last] = removedId;
+ identifiers.Remove(last);
}
- protected void BeforeAdd(int index)
+ protected void BeforeInsert(int index)
{
- for (int i = index; i < values.Count; i++)
+ for (int i = values.Count - 1; i >= index; i--)
{
- identifiers[i + 1] = identifiers[i];
+ object id = GetIdentifier(i);
+ if (id == null)
+ {
+ identifiers.Remove(i + 1);
+ }
+ else
+ {
+ identifiers[i + 1] = id;
+ }
}
identifiers.Remove(index);
}
@@ -354,6 +355,7 @@
set
{
Write();
+ identifiers.Remove(index);
values[index] = value;
}
}
@@ -361,7 +363,7 @@
public void Insert(int index, object value)
{
Write();
- BeforeAdd(index);
+ BeforeInsert(index);
values.Insert(index, value);
}
@@ -425,7 +427,7 @@
public object SyncRoot
{
- get { return values.SyncRoot; }
+ get { return this; }
}
#endregion
Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/B.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/B.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/B.cs 2010-09-01 17:08:11 UTC (rev 5173)
@@ -0,0 +1,40 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace NHibernate.Test.NHSpecificTest.NH2279
+{
+ public class B
+ {
+ private int? _id;
+ private string _name;
+ private IList<C> _cs = new List<C>();
+
+ public B()
+ {
+ }
+
+ public B(string name)
+ {
+ Name = name;
+ }
+
+ public int? Id
+ {
+ get { return _id; }
+ set { _id = value; }
+ }
+
+ public string Name
+ {
+ get { return _name; }
+ set { _name = value; }
+ }
+
+ public IList<C> Cs
+ {
+ get { return _cs; }
+ set { _cs = value; }
+ }
+ }
+}
Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/C.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/C.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/C.cs 2010-09-01 17:08:11 UTC (rev 5173)
@@ -0,0 +1,32 @@
+using System;
+using System.Collections;
+
+namespace NHibernate.Test.NHSpecificTest.NH2279
+{
+ public class C
+ {
+ private int? _id;
+ private string _name;
+
+ public C()
+ {
+ }
+
+ public C(string name)
+ {
+ Name = name;
+ }
+
+ public int? Id
+ {
+ get { return _id; }
+ set { _id = value; }
+ }
+
+ public string Name
+ {
+ get { return _name; }
+ set { _name = value; }
+ }
+ }
+}
Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Fixture.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Fixture.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Fixture.cs 2010-09-01 17:08:11 UTC (rev 5173)
@@ -0,0 +1,159 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.NH2279
+{
+ [TestFixture]
+ public class Fixture : BugTestCase
+ {
+ protected override void OnTearDown()
+ {
+ using( ISession s = sessions.OpenSession() )
+ {
+ s.Delete( "from A" );
+ s.Flush();
+ }
+ }
+
+ [Test]
+ public void IdBagIndexTracking()
+ {
+ A a = new A("a");
+ a.Name = "first generic type";
+ a.Items.Add("a");
+ a.Items.Add("b");
+ a.Items.Add("c");
+
+ ISession s = OpenSession();
+ s.SaveOrUpdate(a);
+ s.Flush();
+ s.Close();
+
+ s = OpenSession();
+ a = (A)s.Load(typeof(A), a.Id);
+ CollectionAssert.AreEquivalent(new[] {"a", "b", "c"}, a.Items);
+
+ // Add and remove a "transient" item.
+ a.Items.Add("d");
+ a.Items.Remove("d");
+
+ // Remove persisted items and then insert a transient item.
+ a.Items.Remove("b");
+ a.Items.Remove("a");
+ a.Items.Insert(0, "e");
+
+ // Add a couple transient items and insert another transient item between them.
+ a.Items.Add("f");
+ a.Items.Add("g");
+ a.Items.Insert(3, "h");
+
+ // Save and then see if we get what we expect.
+ s.SaveOrUpdate(a);
+ s.Flush();
+ s.Close();
+
+ s = OpenSession();
+ a = (A)s.Load(typeof(A), a.Id);
+ CollectionAssert.AreEquivalent(new [] {"c", "e", "f", "g", "h"}, a.Items);
+
+ // Test changing a value by index directly.
+ a.Items[2] = "i";
+
+ string[] expected = a.Items.Cast<string>().ToArray();
+
+ s.SaveOrUpdate(a);
+ s.Flush();
+ s.Close();
+
+ s = OpenSession();
+ a = (A)s.Load(typeof(A), a.Id);
+ CollectionAssert.AreEquivalent(expected, a.Items);
+
+ s.Flush();
+ s.Close();
+ }
+
+ [Test]
+ public void CartesianProduct()
+ {
+ A a1 = new A("a1");
+ A a2 = new A("a2");
+ B b1 = new B("b1");
+ B b2 = new B("b2");
+ B b3 = new B("b3");
+ B b4 = new B("b4");
+ C c1 = new C("c1");
+ C c2 = new C("c2");
+ C c3 = new C("c3");
+ C c4 = new C("c4");
+ C c5 = new C("c5");
+ C c6 = new C("c6");
+
+ a1.Bs.Add(b1);
+ a2.Bs.Add(b2);
+ a2.Bs.Add(b2);
+
+ a1.Bs.Add(b3);
+ a2.Bs.Add(b3);
+
+ a1.Bs.Add(b4);
+ a2.Bs.Add(b4);
+ a1.Bs.Add(b4);
+ a2.Bs.Add(b4);
+
+ b1.Cs.Add(c1);
+ b2.Cs.Add(c2);
+ b2.Cs.Add(c2);
+ b3.Cs.Add(c3);
+ b3.Cs.Add(c3);
+ b3.Cs.Add(c3);
+ b4.Cs.Add(c4);
+ b4.Cs.Add(c4);
+ b4.Cs.Add(c4);
+ b4.Cs.Add(c4);
+
+ b1.Cs.Add(c5);
+ b2.Cs.Add(c5);
+ b3.Cs.Add(c5);
+ b4.Cs.Add(c5);
+
+ b1.Cs.Add(c6);
+ b2.Cs.Add(c6);
+ b3.Cs.Add(c6);
+ b4.Cs.Add(c6);
+ b1.Cs.Add(c6);
+ b2.Cs.Add(c6);
+ b3.Cs.Add(c6);
+ b4.Cs.Add(c6);
+
+ ISession s = OpenSession();
+ s.Save(a1);
+ s.Save(a2);
+ s.Flush();
+ s.Close();
+
+ s = OpenSession();
+ IList<A> results = s.CreateQuery("from A a join fetch a.Bs b join fetch b.Cs").List<A>().Distinct().ToList();
+
+ Assert.That(results, Has.Count.EqualTo(2));
+ A ta1 = results.Single(a => a.Name == "a1");
+ A ta2 = results.Single(a => a.Name == "a2");
+
+ Assert.That(ta1.Bs.Select(b => b.Name).ToArray(), Is.EquivalentTo(new[] { "b1", "b3", "b4", "b4" }));
+ Assert.That(ta2.Bs.Select(b => b.Name).ToArray(), Is.EquivalentTo(new[] { "b2", "b2", "b3", "b4", "b4" }));
+ B tb1 = ta1.Bs.First(b => b.Name == "b1");
+ B tb2 = ta2.Bs.First(b => b.Name == "b2");
+ B tb3 = ta1.Bs.First(b => b.Name == "b3");
+ B tb4 = ta1.Bs.First(b => b.Name == "b4");
+
+ Assert.That(tb1.Cs.Select(c => c.Name).ToArray(), Is.EquivalentTo(new[] { "c1", "c5", "c6", "c6" }));
+ Assert.That(tb2.Cs.Select(c => c.Name).ToArray(), Is.EquivalentTo(new[] { "c2", "c2", "c5", "c6", "c6" }));
+ Assert.That(tb3.Cs.Select(c => c.Name).ToArray(), Is.EquivalentTo(new[] { "c3", "c3", "c3", "c5", "c6", "c6" }));
+ Assert.That(tb4.Cs.Select(c => c.Name).ToArray(), Is.EquivalentTo(new[] { "c4", "c4", "c4", "c4", "c5", "c6", "c6" }));
+ s.Close();
+ }
+ }
+}
\ No newline at end of file
Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Mappings.hbm.xml
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Mappings.hbm.xml (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2279/Mappings.hbm.xml 2010-09-01 17:08:11 UTC (rev 5173)
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<hibernate-mapping
+ xmlns="urn:nhibernate-mapping-2.2"
+ assembly="NHibernate.Test"
+ namespace="NHibernate.Test.NHSpecificTest.NH2279">
+
+ <class name="A" table="a" lazy="false">
+ <id name="Id" column="id" unsaved-value="null">
+ <generator class="native" />
+ </id>
+ <property name="Name" column="name" />
+ <idbag name="Items" cascade="all-delete-orphan">
+ <collection-id type="Int32" column="item_id">
+ <generator class="increment" />
+ </collection-id>
+ <key column="a_id" />
+ <element type="string" />
+ </idbag>
+ <idbag name="Bs" cascade="all-delete-orphan">
+ <collection-id type="Int32" column="item_id">
+ <generator class="increment" />
+ </collection-id>
+ <key column="a_id" />
+ <many-to-many class="B" column="b_id" />
+ </idbag>
+ </class>
+
+ <class name="B" table="b" lazy="false">
+ <id name="Id" column="id" unsaved-value="null">
+ <generator class="native" />
+ </id>
+ <property name="Name" column="name" />
+ <idbag name="Cs" cascade="all-delete-orphan">
+ <collection-id type="Int32" column="item_id">
+ <generator class="increment" />
+ </collection-id>
+ <key column="b_id" />
+ <many-to-many class="C" column="c_id" />
+ </idbag>
+ </class>
+
+ <class name="C" table="c" lazy="false">
+ <id name="Id" column="id" unsaved-value="null">
+ <generator class="native" />
+ </id>
+ <property name="Name" column="name" />
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-08-31 18:08:13 UTC (rev 5172)
+++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-09-01 17:08:11 UTC (rev 5173)
@@ -463,6 +463,10 @@
<Compile Include="NHSpecificTest\NH2245\Model.cs" />
<Compile Include="NHSpecificTest\NH2266\Domain.cs" />
<Compile Include="NHSpecificTest\NH2266\Fixture.cs" />
+ <Compile Include="NHSpecificTest\NH2279\A.cs" />
+ <Compile Include="NHSpecificTest\NH2279\B.cs" />
+ <Compile Include="NHSpecificTest\NH2279\C.cs" />
+ <Compile Include="NHSpecificTest\NH2279\Fixture.cs" />
<Compile Include="NHSpecificTest\NH2287\Domain.cs" />
<Compile Include="NHSpecificTest\NH2287\Fixture.cs" />
<Compile Include="NHSpecificTest\NH2293\Fixture.cs" />
@@ -1745,6 +1749,7 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<EmbeddedResource Include="NHSpecificTest\NH2224\Mappings.hbm.xml" />
+ <EmbeddedResource Include="NHSpecificTest\NH2279\Mappings.hbm.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NHibernate.ByteCode.Castle\NHibernate.ByteCode.Castle.csproj">
@@ -2588,6 +2593,7 @@
<EmbeddedResource Include="DynamicEntity\Tuplizer\Customer.hbm.xml" />
</ItemGroup>
<ItemGroup>
+ <Folder Include="NHSpecificTest\NH2279" />
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|