|
From: <ric...@us...> - 2010-05-27 09:19:43
|
Revision: 4982
http://nhibernate.svn.sourceforge.net/nhibernate/?rev=4982&view=rev
Author: ricbrown
Date: 2010-05-27 09:19:36 +0000 (Thu, 27 May 2010)
Log Message:
-----------
Fix NH-2199 (Map with element doesn't support nullable types)
Modified Paths:
--------------
trunk/nhibernate/releasenotes.txt
trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs
trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs
trunk/nhibernate/src/NHibernate.DomainModel/Baz.cs
trunk/nhibernate/src/NHibernate.Test/Legacy/FooBarTest.cs
trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
Added Paths:
-----------
trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.cs
trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.hbm.xml
trunk/nhibernate/src/NHibernate.Test/CollectionTest/Parent.cs
Modified: trunk/nhibernate/releasenotes.txt
===================================================================
--- trunk/nhibernate/releasenotes.txt 2010-05-24 20:02:27 UTC (rev 4981)
+++ trunk/nhibernate/releasenotes.txt 2010-05-27 09:19:36 UTC (rev 4982)
@@ -1,3 +1,10 @@
+Build 3.0.0.Alpha1
+=============================
+** Known BREAKING CHANGES from NH2.1.1.GA to NH3.0.0.Alpha1
+ ##### Run time #####
+ * (NH-2199) - null values in maps/dictionaries are no longer silenty ignored/deleted
+
+
Build 2.1.1.GA (rev4814)
=============================
Modified: trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs 2010-05-24 20:02:27 UTC (rev 4981)
+++ trunk/nhibernate/src/NHibernate/Collection/Generic/PersistentGenericMap.cs 2010-05-27 09:19:36 UTC (rev 4982)
@@ -54,7 +54,7 @@
IDictionary<TKey, TValue> sn = (IDictionary<TKey, TValue>)GetSnapshot();
foreach (KeyValuePair<TKey, TValue> e in sn)
{
- if (e.Value != null && !gmap.ContainsKey(e.Key))
+ if (!gmap.ContainsKey(e.Key))
{
object key = e.Key;
deletes.Add(indexIsFormula ? e.Value : key);
@@ -67,7 +67,7 @@
{
IDictionary sn = (IDictionary)GetSnapshot();
KeyValuePair<TKey, TValue> e = (KeyValuePair<TKey, TValue>)entry;
- return e.Value != null && sn[e.Key] == null;
+ return !sn.Contains(e.Key);
}
public override bool NeedsUpdating(object entry, int i, IType elemType)
@@ -75,7 +75,9 @@
IDictionary sn = (IDictionary)GetSnapshot();
KeyValuePair<TKey, TValue> e = (KeyValuePair<TKey, TValue>)entry;
object snValue = sn[e.Key];
- return e.Value != null && snValue != null && elemType.IsDirty(snValue, e.Value, Session);
+ bool isNew = !sn.Contains(e.Key);
+ return e.Value != null && snValue != null && elemType.IsDirty(snValue, e.Value, Session)
+ || (!isNew && ((e.Value == null) != (snValue == null)));
}
public override object GetIndex(object entry, int i, ICollectionPersister persister)
@@ -96,9 +98,14 @@
public override bool EntryExists(object entry, int i)
{
- return ((KeyValuePair<TKey, TValue>)entry).Value != null;
+ return gmap.ContainsKey(((KeyValuePair<TKey, TValue>)entry).Key);
}
+ protected override void AddDuringInitialize(object index, object element)
+ {
+ gmap[(TKey)index] = (TValue)element;
+ }
+
#region IDictionary<TKey,TValue> Members
bool IDictionary<TKey, TValue>.ContainsKey(TKey key)
Modified: trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs
===================================================================
--- trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs 2010-05-24 20:02:27 UTC (rev 4981)
+++ trunk/nhibernate/src/NHibernate/Collection/PersistentMap.cs 2010-05-27 09:19:36 UTC (rev 4982)
@@ -109,13 +109,15 @@
object element = role.ReadElement(rs, owner, descriptor.SuffixedElementAliases, Session);
object index = role.ReadIndex(rs, descriptor.SuffixedIndexAliases, Session);
- if (element != null)
- {
- map[index] = element;
- }
+ AddDuringInitialize(index, element);
return element;
}
+ protected virtual void AddDuringInitialize(object index, object element)
+ {
+ map[index] = element;
+ }
+
public override IEnumerable Entries(ICollectionPersister persister)
{
return map;
@@ -158,7 +160,7 @@
foreach (DictionaryEntry e in sn)
{
object key = e.Key;
- if (e.Value != null && map[key] == null)
+ if (!map.Contains(key))
{
deletes.Add(indexIsFormula ? e.Value : key);
}
@@ -170,7 +172,7 @@
{
IDictionary sn = (IDictionary) GetSnapshot();
DictionaryEntry e = (DictionaryEntry) entry;
- return e.Value != null && sn[e.Key] == null;
+ return !sn.Contains(e.Key);
}
public override bool NeedsUpdating(object entry, int i, IType elemType)
@@ -178,7 +180,9 @@
IDictionary sn = (IDictionary) GetSnapshot();
DictionaryEntry e = (DictionaryEntry) entry;
object snValue = sn[e.Key];
- return e.Value != null && snValue != null && elemType.IsDirty(snValue, e.Value, Session);
+ bool isNew = !sn.Contains(e.Key);
+ return e.Value != null && snValue != null && elemType.IsDirty(snValue, e.Value, Session)
+ || (!isNew && ((e.Value == null) != (snValue == null)));
}
public override object GetIndex(object entry, int i, ICollectionPersister persister)
@@ -216,7 +220,7 @@
public override bool EntryExists(object entry, int i)
{
- return ((DictionaryEntry) entry).Value != null;
+ return map.Contains(((DictionaryEntry) entry).Key);
}
#region IDictionary Members
Modified: trunk/nhibernate/src/NHibernate.DomainModel/Baz.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.DomainModel/Baz.cs 2010-05-24 20:02:27 UTC (rev 4981)
+++ trunk/nhibernate/src/NHibernate.DomainModel/Baz.cs 2010-05-27 09:19:36 UTC (rev 4982)
@@ -391,7 +391,7 @@
StringDateMap = new SortedList();
StringDateMap.Add("now", DateTime.Now);
- StringDateMap.Add("never", null);
+ StringDateMap.Add("never", null); // value is persisted since NH-2199
// according to SQL Server the big bag happened in 1753 ;)
StringDateMap.Add("big bang", new DateTime(1753, 01, 01));
//StringDateMap.Add( "millenium", new DateTime( 2000, 01, 01 ) );
Added: trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.cs 2010-05-27 09:19:36 UTC (rev 4982)
@@ -0,0 +1,227 @@
+using System;
+using System.Collections;
+using NUnit.Framework;
+
+namespace NHibernate.Test.CollectionTest
+{
+ [TestFixture]
+ public class NullableValueTypeElementMapFixture : TestCase
+ {
+ protected override IList Mappings
+ {
+ get { return new[] {"CollectionTest.NullableValueTypeElementMapFixture.hbm.xml"}; }
+ }
+
+ protected override string MappingsAssembly
+ {
+ get { return "NHibernate.Test"; }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var s = sessions.OpenSession())
+ {
+ s.Delete("from Parent");
+ s.Flush();
+ }
+ }
+
+ [Test]
+ public void ShouldOverwriteElementValueWithNull()
+ {
+ Guid parentId;
+ var date = new DateTime(2010, 09, 08);
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = new Parent();
+ parent.TypedDates[0] = date;
+
+ s.Save(parent);
+ parentId = parent.Id;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.TypedDates.Count, Is.EqualTo(1),
+ "Should have one child on first reload");
+
+ Assert.That(parent.TypedDates[0], Is.Not.Null,
+ "Should have value in map for 0 on first reload");
+
+ Assert.That(parent.TypedDates[0].Value, Is.EqualTo(date),
+ "Should have same date as saved in map for 0 on first reload");
+
+ parent.TypedDates[0] = null;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.TypedDates.Count, Is.EqualTo(1),
+ "Should have one child on reload after nulling");
+
+ Assert.That(parent.TypedDates[0], Is.Null,
+ "Should have null value for child on reload after nulling");
+ }
+ }
+
+ [Test]
+ public void ShouldOverwriteNullElementWithValue()
+ {
+ Guid parentId;
+ var date = new DateTime(2010, 09, 08);
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = new Parent();
+ parent.TypedDates[0] = null;
+
+ s.Save(parent);
+ parentId = parent.Id;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.TypedDates.Count, Is.EqualTo(1),
+ "Should have 1 child after first reload");
+
+ Assert.That(parent.TypedDates[0], Is.Null,
+ "Should have null value after first reload");
+
+ parent.TypedDates[0] = date;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.TypedDates.Count, Is.EqualTo(1),
+ "Should have 1 child on reload after setting value");
+
+ Assert.That(parent.TypedDates[0], Is.Not.Null,
+ "Should have child with value on reload after setting value");
+
+ Assert.That(parent.TypedDates[0].Value, Is.EqualTo(date));
+ }
+ }
+
+ [Test]
+ public void ShouldAddAndRemoveNullElements()
+ {
+ Guid parentId;
+ var date = new DateTime(2010, 09, 08);
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = new Parent();
+ parent.TypedDates[0] = null;
+ parent.TypedDates[1] = date;
+
+ s.Save(parent);
+ parentId = parent.Id;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.TypedDates.Count, Is.EqualTo(2));
+ Assert.That(parent.TypedDates[0], Is.Null);
+ Assert.That(parent.TypedDates[1], Is.EqualTo(date));
+
+ parent.TypedDates.Remove(0);
+ parent.TypedDates[2] = null;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.TypedDates.Count, Is.EqualTo(2));
+ Assert.That(parent.TypedDates[1], Is.EqualTo(date));
+ Assert.That(parent.TypedDates[2], Is.Null);
+ }
+ }
+
+ [Test]
+ public void AddRemoveUntypedElements()
+ {
+ Guid parentId;
+ var date = new DateTime(2010, 09, 08);
+
+ int toBeRemoved = 0;
+ int toBeUpdatedToNull = 1;
+ int toRemainNull = 2;
+ int toBeUpdatedFromNull = 3;
+ int toBeAddedNull = 4;
+ int toBeAddedNotNull = 5;
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = new Parent();
+ parent.UntypedDates[toBeRemoved] = null;
+ parent.UntypedDates[toBeUpdatedToNull] = date;
+ parent.UntypedDates[toRemainNull] = null;
+ parent.UntypedDates[toBeUpdatedFromNull] = null;
+
+ s.Save(parent);
+ parentId = parent.Id;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.UntypedDates.Count, Is.EqualTo(4));
+ Assert.That(parent.UntypedDates[toBeRemoved], Is.Null);
+ Assert.That(parent.UntypedDates[toBeUpdatedToNull], Is.EqualTo(date));
+ Assert.That(parent.UntypedDates[toRemainNull], Is.Null);
+ Assert.That(parent.UntypedDates[toBeUpdatedFromNull], Is.Null);
+
+ parent.UntypedDates.Remove(toBeRemoved);
+ parent.UntypedDates[toBeUpdatedToNull] = null;
+ parent.UntypedDates[toBeUpdatedFromNull] = date;
+ parent.UntypedDates[toBeAddedNull] = null;
+ parent.UntypedDates[toBeAddedNotNull] = date;
+ tx.Commit();
+ }
+
+ using (var s = OpenSession())
+ using (var tx = s.BeginTransaction())
+ {
+ var parent = s.Load<Parent>(parentId);
+
+ Assert.That(parent.UntypedDates.Count, Is.EqualTo(5));
+ Assert.That(parent.UntypedDates[toBeUpdatedToNull], Is.Null);
+ Assert.That(parent.UntypedDates[toRemainNull], Is.Null);
+ Assert.That(parent.UntypedDates[toBeUpdatedFromNull], Is.EqualTo(date));
+ Assert.That(parent.UntypedDates[toBeAddedNull], Is.Null);
+ Assert.That(parent.UntypedDates[toBeAddedNotNull], Is.EqualTo(date));
+ }
+ }
+ }
+}
Added: trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.hbm.xml
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.hbm.xml (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/CollectionTest/NullableValueTypeElementMapFixture.hbm.xml 2010-05-27 09:19:36 UTC (rev 4982)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernate.Test" namespace="NHibernate.Test.CollectionTest">
+ <class name="Parent">
+ <id name="Id">
+ <generator class="guid.comb" />
+ </id>
+ <map name="TypedDates">
+ <key column="ParentId" />
+ <index column="Idx" type="int" />
+ <element column="Value" />
+ </map>
+ <map name="UntypedDates">
+ <key column="ParentId" />
+ <index column="Idx" type="int" />
+ <element column="Value" type="System.Nullable`1[[System.DateTime, mscorlib]]" />
+ </map>
+ </class>
+</hibernate-mapping>
Added: trunk/nhibernate/src/NHibernate.Test/CollectionTest/Parent.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/CollectionTest/Parent.cs (rev 0)
+++ trunk/nhibernate/src/NHibernate.Test/CollectionTest/Parent.cs 2010-05-27 09:19:36 UTC (rev 4982)
@@ -0,0 +1,19 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace NHibernate.Test.CollectionTest
+{
+ public class Parent
+ {
+ public virtual Guid Id { get; set; }
+ public virtual IDictionary<int, DateTime?> TypedDates { get; set; }
+ public virtual IDictionary UntypedDates { get; set; }
+
+ public Parent()
+ {
+ TypedDates = new Dictionary<int, DateTime?>();
+ UntypedDates = new Hashtable();
+ }
+ }
+}
Modified: trunk/nhibernate/src/NHibernate.Test/Legacy/FooBarTest.cs
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/Legacy/FooBarTest.cs 2010-05-24 20:02:27 UTC (rev 4981)
+++ trunk/nhibernate/src/NHibernate.Test/Legacy/FooBarTest.cs 2010-05-27 09:19:36 UTC (rev 4982)
@@ -825,7 +825,7 @@
list = s.CreateQuery("select index(date) from Baz baz join baz.StringDateMap date").List();
Console.WriteLine(list);
- Assert.AreEqual(2, list.Count);
+ Assert.AreEqual(3, list.Count);
s.CreateQuery(
"from foo in class Foo where foo.Integer not between 1 and 5 and foo.String not in ('cde', 'abc') and foo.String is not null and foo.Integer<=3")
Modified: trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj
===================================================================
--- trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-05-24 20:02:27 UTC (rev 4981)
+++ trunk/nhibernate/src/NHibernate.Test/NHibernate.Test.csproj 2010-05-27 09:19:36 UTC (rev 4982)
@@ -126,6 +126,8 @@
<Compile Include="Classic\Video.cs" />
<Compile Include="CollectionTest\A.cs" />
<Compile Include="CollectionTest\IdBagFixture.cs" />
+ <Compile Include="CollectionTest\NullableValueTypeElementMapFixture.cs" />
+ <Compile Include="CollectionTest\Parent.cs" />
<Compile Include="CompositeCollection\BaseClassA.cs" />
<Compile Include="CompositeCollection\BaseClassB.cs" />
<Compile Include="CompositeCollection\ChildClassA.cs" />
@@ -2135,6 +2137,7 @@
<EmbeddedResource Include="Criteria\Lambda\Mappings.hbm.xml" />
<EmbeddedResource Include="CfgTest\Loquacious\EntityToCache.hbm.xml" />
<EmbeddedResource Include="DriverTest\SqlServerCeEntity.hbm.xml" />
+ <EmbeddedResource Include="CollectionTest\NullableValueTypeElementMapFixture.hbm.xml" />
<Content Include="DynamicEntity\package.html" />
<EmbeddedResource Include="NHSpecificTest\NH2201\Mappings.hbm.xml" />
<EmbeddedResource Include="NHSpecificTest\NH2192\Mappings.hbm.xml" />
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|