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. |