Skip to content

Commit 9bfe1d9

Browse files
author
Dominic Ullmann
committed
NHE-163: Use new ILazyInitializedCollection provided by NHibernate.
By this we can use NHibernateUtil to force initialize collection proxies.
1 parent a89fcd7 commit 9bfe1d9

File tree

8 files changed

+307
-1
lines changed

8 files changed

+307
-1
lines changed

Src/NHibernate.Envers.Tests/NHibernate.Envers.Tests.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@
139139
<Compile Include="NetSpecific\Integration\CustomMapping\ImplicitEnversCollectionType\ChildCollectionType.cs" />
140140
<Compile Include="NetSpecific\Integration\CustomMapping\ImplicitEnversCollectionType\Fixture.cs" />
141141
<Compile Include="NetSpecific\Integration\CustomMapping\ImplicitEnversCollectionType\Model.cs" />
142+
<Compile Include="NetSpecific\Integration\ForceInitialize\Related.cs" />
143+
<Compile Include="NetSpecific\Integration\ForceInitialize\Child.cs" />
144+
<Compile Include="NetSpecific\Integration\ForceInitialize\ForceInitializeTest.cs" />
145+
<Compile Include="NetSpecific\Integration\ForceInitialize\Parent.cs" />
142146
<Compile Include="NetSpecific\Integration\MultiLevelInheritance\Child.cs" />
143147
<Compile Include="NetSpecific\Integration\MultiLevelInheritance\Parent.cs" />
144148
<Compile Include="NetSpecific\Integration\NotFoundIgnore\BaseType\Child.cs" />
@@ -804,6 +808,7 @@
804808
<Compile Include="ValidityTestBase.cs" />
805809
</ItemGroup>
806810
<ItemGroup>
811+
<EmbeddedResource Include="NetSpecific\Integration\ForceInitialize\Mapping.hbm.xml" />
807812
<EmbeddedResource Include="NetSpecific\Integration\BidirectionalList\DifferentAccessTest\Mapping.hbm.xml" />
808813
<EmbeddedResource Include="NetSpecific\Integration\ModifiedFlags\CollectionItemChangingParent\Mapping.hbm.xml" />
809814
<EmbeddedResource Include="NetSpecific\Integration\MultiLevelInheritance\Mapping.hbm.xml" />
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using NHibernate.Envers.Configuration.Attributes;
3+
4+
namespace NHibernate.Envers.Tests.NetSpecific.Integration.ForceInitialize
5+
{
6+
[Audited]
7+
public class Child
8+
{
9+
public virtual Guid Id { get; set; }
10+
public virtual string Str { get; set; }
11+
public virtual string SomeFormula { get; protected set; }
12+
13+
public override bool Equals(object obj)
14+
{
15+
if (ReferenceEquals(null, obj)) return false;
16+
if (ReferenceEquals(this, obj)) return true;
17+
if (obj.GetType() != typeof (Child)) return false;
18+
return Equals((Child) obj);
19+
}
20+
21+
public virtual bool Equals(Child other)
22+
{
23+
if (ReferenceEquals(null, other)) return false;
24+
if (ReferenceEquals(this, other)) return true;
25+
return other.Id.Equals(Id) && Equals(other.Str, Str);
26+
}
27+
28+
public override int GetHashCode()
29+
{
30+
unchecked
31+
{
32+
return (Id.GetHashCode()*397) ^ (Str != null ? Str.GetHashCode() : 0);
33+
}
34+
}
35+
}
36+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using NUnit.Framework;
5+
using SharpTestsEx;
6+
7+
namespace NHibernate.Envers.Tests.NetSpecific.Integration.ForceInitialize
8+
{
9+
public class ForceInitializeTest : TestBase
10+
{
11+
12+
private Guid parentId;
13+
private Guid child1Id;
14+
private Guid relatedId;
15+
16+
public ForceInitializeTest(string strategyType) : base(strategyType)
17+
{
18+
}
19+
20+
protected override void Initialize()
21+
{
22+
var parent = new Parent { Children = new HashSet<Child>(), Data = 1 };
23+
24+
25+
//revision 1
26+
using (var tx = Session.BeginTransaction())
27+
{
28+
parentId = (Guid)Session.Save(parent);
29+
tx.Commit();
30+
}
31+
32+
//revision 2
33+
using (var tx = Session.BeginTransaction())
34+
{
35+
var relatedEntity = new Related {Str = "R_1_1"};
36+
var strTestEntity = new Child { Str = "1_1" };
37+
parent.RelatedEntity = relatedEntity;
38+
parent.Children.Add(strTestEntity);
39+
tx.Commit();
40+
child1Id = strTestEntity.Id;
41+
relatedId = relatedEntity.Id;
42+
}
43+
// revision 3
44+
using (var tx = Session.BeginTransaction())
45+
{
46+
parent.RelatedEntity.Str = "R_1_2";
47+
parent.Children.First().Str = "1_2";
48+
tx.Commit();
49+
}
50+
}
51+
52+
[Test]
53+
public void InitializeManyToOneNull()
54+
{
55+
var parent = AuditReader().Find<Parent>(parentId, 1);
56+
NHibernateUtil.IsInitialized(parent.RelatedEntity).Should().Be.True();
57+
parent.RelatedEntity.Should().Be.Null();
58+
}
59+
60+
[Test]
61+
public void InitializeManyToOneNotNull()
62+
{
63+
var parent = AuditReader().Find<Parent>(parentId, 2);
64+
NHibernateUtil.IsInitialized(parent.RelatedEntity).Should().Be.False();
65+
NHibernateUtil.Initialize(parent.RelatedEntity);
66+
NHibernateUtil.IsInitialized(parent.RelatedEntity).Should().Be.True();
67+
parent.RelatedEntity.Id.Should().Be.EqualTo(relatedId);
68+
parent.RelatedEntity.Str.Should().Be.EqualTo("R_1_1");
69+
}
70+
71+
[Test]
72+
public void InitializeOneToManyEmpty()
73+
{
74+
var parent = AuditReader().Find<Parent>(parentId, 1);
75+
76+
NHibernateUtil.IsInitialized(parent.Children).Should().Be.False();
77+
NHibernateUtil.Initialize(parent.Children);
78+
NHibernateUtil.IsInitialized(parent.Children).Should().Be.True();
79+
80+
parent.Children.Should().Be.Empty();
81+
}
82+
83+
[Test]
84+
public void InitializeOneToManyNotEmpty()
85+
{
86+
var parent = AuditReader().Find<Parent>(parentId, 3);
87+
88+
NHibernateUtil.IsInitialized(parent.Children).Should().Be.False();
89+
NHibernateUtil.Initialize(parent.Children);
90+
NHibernateUtil.IsInitialized(parent.Children).Should().Be.True();
91+
92+
var children = parent.Children.ToList();
93+
children.Should().Have.Count.EqualTo(1);
94+
children[0].Id.Should().Be.EqualTo(child1Id);
95+
children[0].Str.Should().Be.EqualTo("1_2");
96+
}
97+
98+
[Test]
99+
public void CanReuseCollectionProxyAsNormalEntity()
100+
{
101+
using (var tx = Session.BeginTransaction())
102+
{
103+
var ver3 = AuditReader().Find<Parent>(parentId, 3);
104+
ver3.Children.First().Str = "1_3";
105+
var newChild = new Child { Str = "2_1" };
106+
ver3.Children.Add(newChild);
107+
Session.Merge(ver3);
108+
tx.Commit();
109+
}
110+
using (Session.BeginTransaction())
111+
{
112+
var afterMerge = Session.Get<Parent>(parentId);
113+
afterMerge.Children.Count.Should().Be.EqualTo(2);
114+
afterMerge.Children.First().Id.Should().Be.EqualTo(child1Id);
115+
afterMerge.Children.First().Str.Should().Be.EqualTo("1_3");
116+
afterMerge.Children.Last().Str.Should().Be.EqualTo("2_1");
117+
}
118+
}
119+
120+
[Test]
121+
public void NoUpdatesIfMergingNonChangingEntity()
122+
{
123+
using (var tx = Session.BeginTransaction())
124+
{
125+
var ver3 = AuditReader().Find<Parent>(parentId, 3);
126+
Session.Merge(ver3);
127+
Session.SessionFactory.Statistics.Clear();
128+
tx.Commit();
129+
130+
Session.SessionFactory.Statistics.PrepareStatementCount
131+
.Should().Be.EqualTo(0);
132+
}
133+
}
134+
135+
protected override void AddToConfiguration(Cfg.Configuration configuration)
136+
{
137+
configuration.Properties[NHibernate.Cfg.Environment.GenerateStatistics] = "true";
138+
}
139+
140+
}
141+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
2+
assembly="NHibernate.Envers.Tests"
3+
namespace="NHibernate.Envers.Tests.NetSpecific.Integration.ForceInitialize">
4+
<class name="Parent">
5+
<id name="Id">
6+
<generator class="guid.comb"/>
7+
</id>
8+
<property name="Data" />
9+
<set name="Children" cascade="all-delete-orphan">
10+
<key column="parentId" not-null="true" />
11+
<one-to-many class="Child"/>
12+
</set>
13+
<many-to-one name="RelatedEntity" column="RelatedId" cascade="all-delete-orphan"/>
14+
</class>
15+
16+
<class name="Related">
17+
<id name="Id">
18+
<generator class="guid.comb"/>
19+
</id>
20+
<property name="Str"/>
21+
</class>
22+
23+
<class name="Child">
24+
<id name="Id">
25+
<generator class="guid.comb"/>
26+
</id>
27+
<property name="Str"/>
28+
<property name="SomeFormula" formula="[Str] + ' -TEST'"/>
29+
</class>
30+
</hibernate-mapping>
31+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using NHibernate.Envers.Configuration.Attributes;
4+
5+
namespace NHibernate.Envers.Tests.NetSpecific.Integration.ForceInitialize
6+
{
7+
[Audited]
8+
public class Parent
9+
{
10+
public virtual Guid Id { get; set; }
11+
public virtual ISet<Child> Children { get; set; }
12+
13+
public virtual Related RelatedEntity { get; set; }
14+
public virtual int Data { get; set; }
15+
16+
public override bool Equals(object obj)
17+
{
18+
if (ReferenceEquals(null, obj)) return false;
19+
if (ReferenceEquals(this, obj)) return true;
20+
if (obj.GetType() != typeof (Parent)) return false;
21+
return Equals((Parent) obj);
22+
}
23+
24+
public virtual bool Equals(Parent other)
25+
{
26+
if (ReferenceEquals(null, other)) return false;
27+
if (ReferenceEquals(this, other)) return true;
28+
return other.Id.Equals(Id) && other.Data == Data;
29+
}
30+
31+
public override int GetHashCode()
32+
{
33+
unchecked
34+
{
35+
return (Id.GetHashCode()*397) ^ Data;
36+
}
37+
}
38+
}
39+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using System;
2+
using NHibernate.Envers.Configuration.Attributes;
3+
4+
namespace NHibernate.Envers.Tests.NetSpecific.Integration.ForceInitialize
5+
{
6+
[Audited]
7+
public class Related
8+
{
9+
public virtual Guid Id { get; set; }
10+
public virtual string Str { get; set; }
11+
12+
public override bool Equals(object obj)
13+
{
14+
if (ReferenceEquals(null, obj)) return false;
15+
if (ReferenceEquals(this, obj)) return true;
16+
if (obj.GetType() != typeof (Child)) return false;
17+
return Equals((Child) obj);
18+
}
19+
20+
public virtual bool Equals(Child other)
21+
{
22+
if (ReferenceEquals(null, other)) return false;
23+
if (ReferenceEquals(this, other)) return true;
24+
return other.Id.Equals(Id) && Equals(other.Str, Str);
25+
}
26+
27+
public override int GetHashCode()
28+
{
29+
unchecked
30+
{
31+
return (Id.GetHashCode()*397) ^ (Str != null ? Str.GetHashCode() : 0);
32+
}
33+
}
34+
}
35+
}

Src/NHibernate.Envers/Entities/Mapper/Relation/Lazy/Proxy/CollectionProxyInterceptor.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Reflection;
3+
using NHibernate.Collection;
24
using NHibernate.Envers.Entities.Mapper.Relation.Lazy.Initializor;
35
using NHibernate.Proxy.DynamicProxy;
46

@@ -11,13 +13,28 @@ public class CollectionProxyInterceptor : NHibernate.Proxy.DynamicProxy.IInterce
1113
private readonly IInitializor _initializor;
1214
private object _target;
1315

16+
private static readonly MethodInfo forceInitMethod = typeof(ILazyInitializedCollection).GetMethod("ForceInitialization");
17+
private static readonly MethodInfo wasInitializedMethod = typeof(ILazyInitializedCollection).GetProperty("WasInitialized").GetMethod;
18+
19+
1420
public CollectionProxyInterceptor(IInitializor initializor)
1521
{
1622
_initializor = initializor;
1723
}
1824

1925
public object Intercept(InvocationInfo info)
2026
{
27+
// the following methods of ILazyInitializedCollection are implemented to support force initialization using EnversUtil.Initialize
28+
if (info.TargetMethod == forceInitMethod)
29+
{
30+
getOrInitTarget();
31+
return null;
32+
}
33+
if (info.TargetMethod == wasInitializedMethod)
34+
{
35+
return _target != null;
36+
}
37+
2138
return info.TargetMethod.Invoke(getOrInitTarget(), info.Arguments);
2239
}
2340

Src/NHibernate.Envers/Entities/Mapper/Relation/Lazy/Proxy/DefaultEnversProxyFactory.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
2+
using NHibernate.Collection;
23
using NHibernate.Engine;
34
using NHibernate.Envers.Configuration;
45
using NHibernate.Envers.Entities.Mapper.Relation.Lazy.Initializor;
56
using NHibernate.Envers.Reader;
7+
using NHibernate.Proxy;
68
using NHibernate.Proxy.DynamicProxy;
79

810
namespace NHibernate.Envers.Entities.Mapper.Relation.Lazy.Proxy
@@ -17,7 +19,7 @@ public class DefaultEnversProxyFactory : IEnversProxyFactory
1719

1820
public object CreateCollectionProxy(System.Type collectionInterface, IInitializor collectionInitializor)
1921
{
20-
return proxyFactory.CreateProxy(collectionInterface, new CollectionProxyInterceptor(collectionInitializor));
22+
return proxyFactory.CreateProxy(collectionInterface, new CollectionProxyInterceptor(collectionInitializor), typeof(ILazyInitializedCollection));
2123
}
2224

2325
public object CreateToOneProxy(AuditConfiguration verCfg, IAuditReaderImplementor versionsReader,

0 commit comments

Comments
 (0)