From: <fab...@us...> - 2011-03-28 16:03:13
|
Revision: 5565 http://nhibernate.svn.sourceforge.net/nhibernate/?rev=5565&view=rev Author: fabiomaulo Date: 2011-03-28 16:03:06 +0000 (Mon, 28 Mar 2011) Log Message: ----------- Fix NH-2489 Modified Paths: -------------- trunk/nhibernate/releasenotes.txt trunk/nhibernate/src/NHibernate/Collection/AbstractPersistentCollection.cs trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericList.cs trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs trunk/nhibernate/src/NHibernate/Collection/PersistentList.cs trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs trunk/nhibernate/src/NHibernate/Persister/Collection/ICollectionPersister.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/SetFixture.cs trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj Added Paths: ----------- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Fixture.cs trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Mappings.hbm.xml trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Model.cs Modified: trunk/nhibernate/releasenotes.txt =================================================================== --- trunk/nhibernate/releasenotes.txt 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/releasenotes.txt 2011-03-28 16:03:06 UTC (rev 5565) @@ -5,6 +5,7 @@ ##### Possible Breaking Changes ##### * [NH-2550] - Allow public access to FieldInterceptor Session (IFieldInterceptor changed) * [NH-2593] - Form Microsoft SQL Server the default batch-size (adonet.batch_size) is set to 20 where not explicit defined in the session-factory configuration + * - ICollectionPersister added property to fix [NH-2489] Build 3.1.0.GA (rev5425) ============================= Modified: trunk/nhibernate/src/NHibernate/Collection/AbstractPersistentCollection.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Collection/AbstractPersistentCollection.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Collection/AbstractPersistentCollection.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -19,6 +19,7 @@ public abstract class AbstractPersistentCollection : IPersistentCollection { protected internal static readonly object Unknown = new object(); //place holder + protected internal static readonly object NotFound = new object(); //place holder protected interface IDelayedOperation { @@ -345,7 +346,8 @@ { session.Flush(); } - return persister.GetElementByIndex(entry.LoadedKey, index, session, owner); + var elementByIndex = persister.GetElementByIndex(entry.LoadedKey, index, session, owner); + return persister.NotFoundObject == elementByIndex ? NotFound : elementByIndex; } } Read(); Modified: trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericList.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericList.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericList.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -88,10 +88,16 @@ { return glist[index]; } - else + if (result == NotFound) { - return (T) result; + // check if the index is valid + if (index >= Count) + { + throw new ArgumentOutOfRangeException("index"); + } + return default(T); } + return (T) result; } set { @@ -107,7 +113,7 @@ } else { - QueueOperation(new SetDelayedOperation(this, index, value, old)); + QueueOperation(new SetDelayedOperation(this, index, value, old == NotFound ? null : old)); } } } Modified: trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -125,7 +125,7 @@ object old = ReadElementByIndex(key); if (old != Unknown) { - QueueOperation(new PutDelayedOperation(this, key, value, old)); + QueueOperation(new PutDelayedOperation(this, key, value, old == NotFound ? null : old)); return; } } @@ -139,7 +139,7 @@ if (PutQueueEnabled) { object old = ReadElementByIndex(key); - QueueOperation(new RemoveDelayedOperation(this, key, old)); + QueueOperation(new RemoveDelayedOperation(this, key, old == NotFound ? null : old)); return true; } else @@ -161,11 +161,13 @@ { return gmap.TryGetValue(key, out value); } - else + if(result == NotFound) { - value = (TValue)result; - return true; + value = default(TValue); + return false; } + value = (TValue)result; + return true; } TValue IDictionary<TKey, TValue>.this[TKey key] @@ -173,7 +175,15 @@ get { object result = ReadElementByIndex(key); - return result == Unknown ? gmap[key] : (TValue)result; + if (result == Unknown) + { + return gmap[key]; + } + if (result == NotFound) + { + throw new KeyNotFoundException(); + } + return (TValue) result; } set { @@ -183,7 +193,7 @@ object old = ReadElementByIndex(key); if (old != Unknown) { - QueueOperation(new PutDelayedOperation(this, key, value, old)); + QueueOperation(new PutDelayedOperation(this, key, value, old == NotFound ? null : old)); return; } } Modified: trunk/nhibernate/src/NHibernate/Collection/PersistentList.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Collection/PersistentList.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Collection/PersistentList.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -339,7 +339,7 @@ } else { - QueueOperation(new RemoveDelayedOperation(this, index, old)); + QueueOperation(new RemoveDelayedOperation(this, index, old == NotFound ? null : old)); } } @@ -352,7 +352,20 @@ throw new IndexOutOfRangeException("negative index"); } object result = ReadElementByIndex(index); - return result == Unknown ? list[index] : result; + if (result == Unknown) + { + return list[index]; + } + if(NotFound == result) + { + // check if the index is valid + if(index >= Count) + { + throw new ArgumentOutOfRangeException("index"); + } + return null; + } + return result; } set { @@ -360,7 +373,19 @@ { throw new IndexOutOfRangeException("negative index"); } - object old = PutQueueEnabled ? ReadElementByIndex(index) : Unknown; + object old; + if (PutQueueEnabled) + { + old = ReadElementByIndex(index); + if(old == NotFound) + { + old = null; + } + } + else + { + old = Unknown; + } if (old == Unknown) { Write(); Modified: trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -242,7 +242,7 @@ object old = ReadElementByIndex(key); if (old != Unknown) { - QueueOperation(new PutDelayedOperation(this, key, value, old)); + QueueOperation(new PutDelayedOperation(this, key, value, old == NotFound ? null : old)); return; } } @@ -280,7 +280,10 @@ if (PutQueueEnabled) { object old = ReadElementByIndex(key); - QueueOperation(new RemoveDelayedOperation(this, key, old)); + if (old != NotFound) + { + QueueOperation(new RemoveDelayedOperation(this, key, old)); + } return; } else @@ -302,7 +305,7 @@ get { object result = ReadElementByIndex(key); - return result == Unknown ? map[key] : result; + return result == Unknown ? map[key] : (result == NotFound ? null : result); } set { @@ -310,7 +313,7 @@ if (PutQueueEnabled) { object old = ReadElementByIndex(key); - if (old != Unknown) + if (old != Unknown && old != NotFound) { QueueOperation(new PutDelayedOperation(this, key, value, old)); return; Modified: trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -34,6 +34,7 @@ public abstract class AbstractCollectionPersister : ICollectionMetadata, ISqlLoadableCollection, IPostInsertIdentityPersister { + protected static readonly object NotFoundPlaceHolder = new object(); private readonly string role; #region SQL statements @@ -1617,7 +1618,7 @@ } else { - return null; + return NotFoundObject; } } finally @@ -1638,6 +1639,11 @@ } } + public object NotFoundObject + { + get { return NotFoundPlaceHolder; } + } + public abstract bool ConsumesEntityAlias(); public abstract SqlString FromJoinFragment(string alias, bool innerJoin, bool includeSubclasses); Modified: trunk/nhibernate/src/NHibernate/Persister/Collection/ICollectionPersister.cs =================================================================== --- trunk/nhibernate/src/NHibernate/Persister/Collection/ICollectionPersister.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate/Persister/Collection/ICollectionPersister.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -273,6 +273,20 @@ int GetSize(object key, ISessionImplementor session); bool IndexExists(object key, object index, ISessionImplementor session); bool ElementExists(object key, object element, ISessionImplementor session); + + /// <summary> + /// Try to find an element by a given index. + /// </summary> + /// <param name="key">The key of the collection (collection-owner identifier)</param> + /// <param name="index">The given index.</param> + /// <param name="session">The active <see cref="ISession"/>.</param> + /// <param name="owner">The owner of the collection.</param> + /// <returns>The value of the element where available; otherwise <see cref="NotFoundObject"/>.</returns> object GetElementByIndex(object key, object index, ISessionImplementor session, object owner); + + /// <summary> + /// A place-holder to inform that the data-reader was empty. + /// </summary> + object NotFoundObject { get; } } } Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Fixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Fixture.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Fixture.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using SharpTestsEx; + +namespace NHibernate.Test.NHSpecificTest.NH2489 +{ + public class Fixture : BugTestCase + { + #region Scenarios + + private class ListScenario : IDisposable + { + private readonly ISessionFactory factory; + + public ListScenario(ISessionFactory factory) + { + this.factory = factory; + using (ISession s = factory.OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + var entity = new Base(); + var child = new Child(); + // null members are partially working for lists, can't have one at the end + // and can't use the Count property. + entity.Children = new List<Child> {null, child}; + s.Save(entity); + t.Commit(); + } + } + } + + public void Dispose() + { + using (ISession s = factory.OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + s.Delete("from Base"); + s.Delete("from Child"); + t.Commit(); + } + } + } + } + + private class MapScenario : IDisposable + { + private readonly ISessionFactory factory; + + public MapScenario(ISessionFactory factory) + { + this.factory = factory; + using (ISession s = factory.OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + var entity = new Base(); + entity.NamedChildren = new Dictionary<string, Child> + { + {"Child1", new Child()}, + {"NullChild", null}, + }; + s.Save(entity); + t.Commit(); + } + } + } + + public void Dispose() + { + using (ISession s = factory.OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + s.Delete("from Base"); + s.Delete("from Child"); + t.Commit(); + } + } + } + } + + #endregion + + [Test] + public void List_InvalidIndex() + { + using (new ListScenario(Sfi)) + { + using (ISession s = OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + // accessing an invalid index should throw an exception + var entity = s.CreateQuery("from Base").UniqueResult<Base>(); + // null collection members don't seem to work, at least for lazy="extra" collections + entity.Children.Count.Should().Be.EqualTo(2); + NHibernateUtil.IsInitialized(entity.Children).Should().Be.False(); + Executing.This(() => { Child ignored = entity.Children[2]; }).Should().Throw<ArgumentOutOfRangeException>(); + } + } + } + } + + [Test] + public void List_NullChild() + { + using (new ListScenario(Sfi)) + { + using (ISession s = OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + // the list should only contain an item at index 0 + // accessing an invalid index should throw an exception + var entity = s.CreateQuery("from Base").UniqueResult<Base>(); + // null collection members don't seem to work, at least for lazy="extra" collections + entity.Children.Count.Should().Not.Be.EqualTo(0); + //entity.Children.Count.Should().Be.EqualTo(2); + NHibernateUtil.IsInitialized(entity.Children).Should().Be.False(); + var sigil = new Child(); + Child child = sigil; + Executing.This(() => { child = entity.Children[0]; }).Should().NotThrow(); + child.Should().Not.Be.EqualTo(sigil); + child.Should().Be.Null(); + } + } + } + } + + [Test] + public void Map_Item() + { + using (new MapScenario(Sfi)) + { + using (ISession s = OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + // accessing an invalid key should fail or throw an exception, depending on method + var entity = s.CreateQuery("from Base").UniqueResult<Base>(); + // null collection members don't seem to work, at least for lazy="extra" collections + entity.NamedChildren.Count.Should().Be.EqualTo(2); + NHibernateUtil.IsInitialized(entity.NamedChildren).Should().Be.False(); + Executing.This(() => { Child ignored = entity.NamedChildren["InvalidKey"]; }).Should().Throw<KeyNotFoundException>(); + } + } + } + } + + [Test] + public void Map_TryGetValue_Invalid() + { + using (new MapScenario(Sfi)) + { + using (ISession s = OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + // accessing an invalid key should fail or throw an exception, depending on method + var entity = s.CreateQuery("from Base").UniqueResult<Base>(); + // null collection members don't seem to work, at least for lazy="extra" collections + entity.NamedChildren.Count.Should().Be.EqualTo(2); + NHibernateUtil.IsInitialized(entity.NamedChildren).Should().Be.False(); + Child child; + entity.NamedChildren.TryGetValue("InvalidKey", out child).Should().Be.False(); + child.Should().Be.Null(); + } + } + } + } + + [Test] + public void Map_NullChild() + { + using (new MapScenario(Sfi)) + { + using (ISession s = OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + var entity = s.CreateQuery("from Base").UniqueResult<Base>(); + // null collection members don't seem to work, at least for lazy="extra" collections + entity.NamedChildren.Count.Should().Not.Be.EqualTo(0); + //entity.NamedChildren.Count.Should().Be.EqualTo(2); + NHibernateUtil.IsInitialized(entity.NamedChildren).Should().Be.False(); + // null valued child shouldn't cause errors + var sigil = new Child(); + Child child = sigil; + Assert.DoesNotThrow(() => { child = entity.NamedChildren["NullChild"]; }); + child.Should().Not.Be.EqualTo(sigil); + child.Should().Be.Null(); + } + } + } + } + + [Test] + public void Map_NullChild_TryGetValue() + { + using (new MapScenario(Sfi)) + { + using (ISession s = OpenSession()) + { + using (ITransaction t = s.BeginTransaction()) + { + var entity = s.CreateQuery("from Base").UniqueResult<Base>(); + // null collection members don't seem to work, at least for lazy="extra" collections + entity.NamedChildren.Count.Should().Not.Be.EqualTo(0); + //entity.NamedChildren.Count.Should().Be.EqualTo(2); + // null valued child shouldn't cause errors + NHibernateUtil.IsInitialized(entity.NamedChildren).Should().Be.False(); + Child child; + entity.NamedChildren.TryGetValue("NullChild", out child) + .Should().Be.True(); + child.Should().Be.Null(); + } + } + } + } + } +} \ No newline at end of file Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Mappings.hbm.xml =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Mappings.hbm.xml (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Mappings.hbm.xml 2011-03-28 16:03:06 UTC (rev 5565) @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8" ?> +<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" + namespace="NHibernate.Test.NHSpecificTest.NH2489" + assembly="NHibernate.Test" +> + <class name="Base" > + <id name="Id"> + <generator class="increment"/> + </id> + <list name="Children" lazy="extra" cascade="all"> + <key column="listparentid"/> + <!-- sqlite doesn't like a column named index --> + <index column="childindex"/> + <many-to-many class="Child"/> + </list> + <map name="NamedChildren" lazy="extra" cascade="all" > + <key column="mapparentid" /> + <index column="name" type="string" /> + <many-to-many class="Child"> + <column name="Child" not-null="false" /> + </many-to-many> + </map> + </class> + + <class name="Child"> + <id name="Id"> + <generator class="increment"/> + </id> + </class> + +</hibernate-mapping> Added: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Model.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Model.cs (rev 0) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/NH2489/Model.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -0,0 +1,34 @@ +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.NH2489 +{ + public class Base + { + public virtual int Id + { + get; + set; + } + + public virtual IList<Child> Children + { + get; + set; + } + + public virtual IDictionary<string, Child> NamedChildren + { + get; + set; + } + } + + public class Child + { + public virtual int Id + { + get; + set; + } + } +} Modified: trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/SetFixture.cs =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/SetFixture.cs 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate.Test/NHSpecificTest/SetFixture.cs 2011-03-28 16:03:06 UTC (rev 5565) @@ -343,6 +343,11 @@ throw new NotImplementedException(); } + public object NotFoundObject + { + get { throw new NotImplementedException(); } + } + public ISessionFactoryImplementor Factory { get { return null; } Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj =================================================================== --- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2011-03-28 15:15:24 UTC (rev 5564) +++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2011-03-28 16:03:06 UTC (rev 5565) @@ -656,6 +656,8 @@ <Compile Include="NHSpecificTest\NH2484\Model.cs" /> <Compile Include="NHSpecificTest\NH2488\Fixture.cs" /> <Compile Include="NHSpecificTest\NH2488\Model.cs" /> + <Compile Include="NHSpecificTest\NH2489\Fixture.cs" /> + <Compile Include="NHSpecificTest\NH2489\Model.cs" /> <Compile Include="NHSpecificTest\NH2490\Fixture.cs" /> <Compile Include="NHSpecificTest\NH2490\Model.cs" /> <Compile Include="NHSpecificTest\NH2491\Fixture.cs" /> @@ -2498,6 +2500,7 @@ <EmbeddedResource Include="NHSpecificTest\NH1291AnonExample\Mappings.hbm.xml" /> </ItemGroup> <ItemGroup> + <EmbeddedResource Include="NHSpecificTest\NH2489\Mappings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH2603\Mappings.hbm.xml" /> <EmbeddedResource Include="Subselect\Beings.hbm.xml" /> <EmbeddedResource Include="NHSpecificTest\NH2488\Mappings.hbm.xml" /> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |