Skip to content

Commit 0b66742

Browse files
Sebazzzhazzik
authored andcommitted
Add check to ensure that IUserCollectionType.Instantiate returns uninitialized collection
1 parent 462c8bd commit 0b66742

File tree

5 files changed

+318
-1
lines changed

5 files changed

+318
-1
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Linq;
12+
using NHibernate.Cfg.MappingSchema;
13+
using NHibernate.Mapping.ByCode;
14+
using NUnit.Framework;
15+
using NHibernate.Linq;
16+
17+
namespace NHibernate.Test.NHSpecificTest.NH3772
18+
{
19+
using System.Threading.Tasks;
20+
[TestFixture]
21+
public class FixtureAsync : TestCaseMappingByCode
22+
{
23+
protected override HbmMapping GetMappings()
24+
{
25+
var mapper = new ModelMapper();
26+
mapper.Class<Entity>(
27+
rc =>
28+
{
29+
rc.Id(x => x.Id, m => m.Generator(Generators.Identity));
30+
rc.Property(x => x.Name);
31+
32+
rc.Set(
33+
x => x.SubEntities,
34+
x => x.Type<CustomGenericCollection<SubEntity>>(),
35+
x => x.ManyToMany());
36+
});
37+
38+
mapper.Class<SubEntity>(
39+
rc =>
40+
{
41+
rc.Id(x => x.Id, m => m.Generator(Generators.Identity));
42+
rc.Property(x => x.Name);
43+
});
44+
45+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
46+
}
47+
48+
protected override void OnSetUp()
49+
{
50+
using (var session = OpenSession())
51+
using (var transaction = session.BeginTransaction())
52+
{
53+
session.Save(new Entity {Name = "Bob"});
54+
55+
session.Save(new Entity {Name = "Sally"});
56+
57+
session.Save(new SubEntity {Name = "Bob"});
58+
59+
session.Save(new SubEntity {Name = "Sally"});
60+
61+
session.Flush();
62+
transaction.Commit();
63+
}
64+
}
65+
66+
protected override void OnTearDown()
67+
{
68+
using (var session = OpenSession())
69+
using (var transaction = session.BeginTransaction())
70+
{
71+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
72+
73+
session.Flush();
74+
75+
transaction.Commit();
76+
}
77+
}
78+
79+
[Test]
80+
public void CustomCollectionType_ThrowsHibernateException_WhenUserCollectionTypeReturnsInitializedCollectionAsync()
81+
{
82+
CustomGenericCollection<SubEntity>.InstantiateInitializedCollection = true;
83+
84+
using (var session = OpenSession())
85+
using (session.BeginTransaction())
86+
{
87+
Task<Entity> ActionAsync() => (from e in session.Query<Entity>() where e.Name == "Bob" select e).FirstAsync();
88+
89+
Assert.That(
90+
ActionAsync,
91+
Throws.InstanceOf<HibernateException>().And.Message.EqualTo(
92+
"UserCollectionType.Instantiate should return a non-initialized persistent " +
93+
"collection. Implement UserCollectionType.Instantiate(int anticipatedSize) to " +
94+
"actually create the collection that needs to be wrapped by the persistent collection."));
95+
}
96+
}
97+
98+
[Test]
99+
public void CustomCollectionType_DoesNotThrowHibernateException_WhenUserCollectionTypeReturnsUninitializedCollectionAsync()
100+
{
101+
CustomGenericCollection<SubEntity>.InstantiateInitializedCollection = false;
102+
103+
using (var session = OpenSession())
104+
using (session.BeginTransaction())
105+
{
106+
Task<Entity> ActionAsync() => (from e in session.Query<Entity>() where e.Name == "Bob" select e).FirstAsync();
107+
108+
Assert.That(ActionAsync, Throws.Nothing);
109+
}
110+
}
111+
}
112+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using NHibernate.Collection;
5+
using NHibernate.Collection.Generic;
6+
using NHibernate.Engine;
7+
using NHibernate.Persister.Collection;
8+
using NHibernate.UserTypes;
9+
10+
namespace NHibernate.Test.NHSpecificTest.NH3772
11+
{
12+
public class CustomGenericCollection<T> : IUserCollectionType where T : class
13+
{
14+
public static bool InstantiateInitializedCollection = true;
15+
16+
public IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister)
17+
{
18+
if (InstantiateInitializedCollection)
19+
return new PersistentGenericSet<T>(session, new HashSet<T>(EqualityComparer<T>.Default));
20+
21+
return new PersistentGenericSet<T>(session);
22+
}
23+
24+
public IPersistentCollection Wrap(ISessionImplementor session, object collection)
25+
{
26+
var realCollection = (ISet<T>) collection;
27+
return new PersistentGenericSet<T>(session, realCollection);
28+
}
29+
30+
public IEnumerable GetElements(object collection)
31+
{
32+
var realCollection = (ISet<T>) collection;
33+
return realCollection.ToList();
34+
}
35+
36+
public bool Contains(object collection, object entity)
37+
{
38+
var realCollection = (ISet<T>) collection;
39+
return realCollection.Contains((T) entity);
40+
}
41+
42+
public object IndexOf(object collection, object entity)
43+
{
44+
return -1; // no indexing supported
45+
}
46+
47+
public object ReplaceElements(
48+
object original,
49+
object target,
50+
ICollectionPersister persister,
51+
object owner,
52+
IDictionary copyCache,
53+
ISessionImplementor session)
54+
{
55+
var originalCollection = (ISet<T>) original;
56+
var targetCollection = (ISet<T>) target;
57+
58+
targetCollection.Clear();
59+
targetCollection.UnionWith(originalCollection);
60+
61+
return targetCollection;
62+
}
63+
64+
public object Instantiate(int anticipatedSize)
65+
{
66+
return new HashSet<T>(EqualityComparer<T>.Default);
67+
}
68+
}
69+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
3+
namespace NHibernate.Test.NHSpecificTest.NH3772
4+
{
5+
public class Entity
6+
{
7+
private ICollection<SubEntity> _subEntities;
8+
public virtual int Id { get; set; }
9+
public virtual string Name { get; set; }
10+
11+
public virtual ICollection<SubEntity> SubEntities
12+
{
13+
get => _subEntities ?? (_subEntities = new HashSet<SubEntity>());
14+
set => _subEntities = value;
15+
}
16+
}
17+
18+
public class SubEntity
19+
{
20+
public virtual int Id { get; set; }
21+
public virtual string Name { get; set; }
22+
}
23+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
using System.Linq;
2+
using NHibernate.Cfg.MappingSchema;
3+
using NHibernate.Mapping.ByCode;
4+
using NUnit.Framework;
5+
6+
namespace NHibernate.Test.NHSpecificTest.NH3772
7+
{
8+
[TestFixture]
9+
public class Fixture : TestCaseMappingByCode
10+
{
11+
protected override HbmMapping GetMappings()
12+
{
13+
var mapper = new ModelMapper();
14+
mapper.Class<Entity>(
15+
rc =>
16+
{
17+
rc.Id(x => x.Id, m => m.Generator(Generators.Identity));
18+
rc.Property(x => x.Name);
19+
20+
rc.Set(
21+
x => x.SubEntities,
22+
x => x.Type<CustomGenericCollection<SubEntity>>(),
23+
x => x.ManyToMany());
24+
});
25+
26+
mapper.Class<SubEntity>(
27+
rc =>
28+
{
29+
rc.Id(x => x.Id, m => m.Generator(Generators.Identity));
30+
rc.Property(x => x.Name);
31+
});
32+
33+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
34+
}
35+
36+
protected override void OnSetUp()
37+
{
38+
using (var session = OpenSession())
39+
using (var transaction = session.BeginTransaction())
40+
{
41+
session.Save(new Entity {Name = "Bob"});
42+
43+
session.Save(new Entity {Name = "Sally"});
44+
45+
session.Save(new SubEntity {Name = "Bob"});
46+
47+
session.Save(new SubEntity {Name = "Sally"});
48+
49+
session.Flush();
50+
transaction.Commit();
51+
}
52+
}
53+
54+
protected override void OnTearDown()
55+
{
56+
using (var session = OpenSession())
57+
using (var transaction = session.BeginTransaction())
58+
{
59+
session.CreateQuery("delete from System.Object").ExecuteUpdate();
60+
61+
session.Flush();
62+
63+
transaction.Commit();
64+
}
65+
}
66+
67+
[Test]
68+
public void CustomCollectionType_ThrowsHibernateException_WhenUserCollectionTypeReturnsInitializedCollection()
69+
{
70+
CustomGenericCollection<SubEntity>.InstantiateInitializedCollection = true;
71+
72+
using (var session = OpenSession())
73+
using (session.BeginTransaction())
74+
{
75+
Entity Action() => (from e in session.Query<Entity>() where e.Name == "Bob" select e).First();
76+
77+
Assert.That(
78+
Action,
79+
Throws.InstanceOf<HibernateException>().And.Message.EqualTo(
80+
"UserCollectionType.Instantiate should return a non-initialized persistent " +
81+
"collection. Implement UserCollectionType.Instantiate(int anticipatedSize) to " +
82+
"actually create the collection that needs to be wrapped by the persistent collection."));
83+
}
84+
}
85+
86+
[Test]
87+
public void CustomCollectionType_DoesNotThrowHibernateException_WhenUserCollectionTypeReturnsUninitializedCollection()
88+
{
89+
CustomGenericCollection<SubEntity>.InstantiateInitializedCollection = false;
90+
91+
using (var session = OpenSession())
92+
using (session.BeginTransaction())
93+
{
94+
Entity Action() => (from e in session.Query<Entity>() where e.Name == "Bob" select e).First();
95+
96+
Assert.That(Action, Throws.Nothing);
97+
}
98+
}
99+
}
100+
}

src/NHibernate/Type/CustomCollectionType.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,20 @@ public IUserCollectionType UserType
4747

4848
public override IPersistentCollection Instantiate(ISessionImplementor session, ICollectionPersister persister, object key)
4949
{
50-
return userType.Instantiate(session, persister);
50+
var createdCollection = userType.Instantiate(session, persister);
51+
EnsureNotInitialized(createdCollection);
52+
return createdCollection;
53+
}
54+
55+
private static void EnsureNotInitialized(IPersistentCollection createdCollection)
56+
{
57+
if (createdCollection.WasInitialized)
58+
{
59+
throw new HibernateException(
60+
"UserCollectionType.Instantiate should return a non-initialized persistent collection. " +
61+
"Implement UserCollectionType.Instantiate(int anticipatedSize) to actually create the collection " +
62+
"that needs to be wrapped by the persistent collection.");
63+
}
5164
}
5265

5366
public override IPersistentCollection Wrap(ISessionImplementor session, object collection)

0 commit comments

Comments
 (0)