Skip to content

Commit f790b99

Browse files
cremoroskarb
authored andcommitted
NH-3383: Fix for multiple objects of CascadeStyle in Memory that should be singleton.
1 parent 5e9e273 commit f790b99

File tree

3 files changed

+192
-24
lines changed

3 files changed

+192
-24
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using System;
2+
using System.IO;
3+
using System.Reflection;
4+
using System.Runtime.Serialization.Formatters.Binary;
5+
using NHibernate.Cfg.MappingSchema;
6+
using NHibernate.Engine;
7+
using NHibernate.Mapping;
8+
using NHibernate.Mapping.ByCode;
9+
using NHibernate.Type;
10+
using NUnit.Framework;
11+
12+
namespace NHibernate.Test.NHSpecificTest.NH3383
13+
{
14+
public class ByCodeFixture : TestCaseMappingByCode
15+
{
16+
protected override HbmMapping GetMappings()
17+
{
18+
var mapper = new ModelMapper();
19+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
20+
}
21+
22+
[Test]
23+
public void DeserializedPropertyMapping_RefersToSameCascadeStyle()
24+
{
25+
var classMapping = CreateMappingClasses();
26+
27+
RootClass deserializedClassMapping;
28+
29+
using (MemoryStream configMemoryStream = new MemoryStream())
30+
{
31+
BinaryFormatter formatter = new BinaryFormatter();
32+
formatter.Serialize(configMemoryStream, classMapping);
33+
configMemoryStream.Position = 0;
34+
deserializedClassMapping = (RootClass)formatter.Deserialize(configMemoryStream);
35+
}
36+
37+
AssertDeserializedMappingClasses(deserializedClassMapping);
38+
}
39+
40+
// This test uses a seperate AppDomain to simulate the loading of a Configuration that was
41+
// serialized to the disk and is later deserialized in a new process.
42+
[Test]
43+
public void DeserializedPropertyMapping_CascadeStyleNotYetInitializedOnDeserialization_RefersToSameCascadeStyle()
44+
{
45+
var classMapping = CreateMappingClasses();
46+
47+
using (MemoryStream configMemoryStream = new MemoryStream())
48+
{
49+
BinaryFormatter formatter = new BinaryFormatter();
50+
formatter.Serialize(configMemoryStream, classMapping);
51+
configMemoryStream.Position = 0;
52+
53+
var secondAppDomain = AppDomain.CreateDomain(
54+
"SecondAppDomain",
55+
null,
56+
AppDomain.CurrentDomain.SetupInformation);
57+
58+
try
59+
{
60+
var helper = (AppDomainHelper)secondAppDomain.CreateInstanceAndUnwrap(
61+
Assembly.GetExecutingAssembly().FullName,
62+
typeof(AppDomainHelper).FullName);
63+
64+
helper.DeserializeAndAssert(configMemoryStream);
65+
}
66+
finally
67+
{
68+
AppDomain.Unload(secondAppDomain);
69+
}
70+
}
71+
}
72+
73+
private static RootClass CreateMappingClasses()
74+
{
75+
var classMapping = new RootClass();
76+
var componentMapping = new NHibernate.Mapping.Component(classMapping);
77+
78+
var componentPropertyMapping = new Property(componentMapping);
79+
componentPropertyMapping.Name = "ComponentPropertyInClass";
80+
classMapping.AddProperty(componentPropertyMapping);
81+
82+
var stringValue = new SimpleValue();
83+
stringValue.TypeName = typeof(string).FullName;
84+
85+
var stringPropertyInComponentMapping = new Property(stringValue);
86+
stringPropertyInComponentMapping.Name = "StringPropertyInComponent";
87+
componentMapping.AddProperty(stringPropertyInComponentMapping);
88+
89+
var componentType = (IAbstractComponentType)componentMapping.Type;
90+
91+
Assume.That(CascadeStyle.None == stringPropertyInComponentMapping.CascadeStyle);
92+
Assume.That(CascadeStyle.None == componentType.GetCascadeStyle(0));
93+
Assume.That(CascadeStyle.None == componentPropertyMapping.CascadeStyle);
94+
95+
return classMapping;
96+
}
97+
98+
private static void AssertDeserializedMappingClasses(RootClass deserializedClassMapping)
99+
{
100+
var deserializedComponentPropertyMapping = deserializedClassMapping.GetProperty("ComponentPropertyInClass");
101+
var deserializedComponentMapping = (NHibernate.Mapping.Component)deserializedComponentPropertyMapping.Value;
102+
var deserializedComponentType = (IAbstractComponentType)deserializedComponentMapping.Type;
103+
var deserializedStringPropertyInComponentMapping = deserializedComponentMapping.GetProperty("StringPropertyInComponent");
104+
105+
// Must be all the same objects since CascadeStyles are singletons and are
106+
// compared with "==" and "!=" operators.
107+
Assert.AreSame(CascadeStyle.None, deserializedStringPropertyInComponentMapping.CascadeStyle);
108+
Assert.AreSame(CascadeStyle.None, deserializedComponentType.GetCascadeStyle(0));
109+
Assert.AreSame(CascadeStyle.None, deserializedComponentPropertyMapping.CascadeStyle);
110+
}
111+
112+
private sealed class AppDomainHelper : MarshalByRefObject
113+
{
114+
public void DeserializeAndAssert(MemoryStream configMemoryStream)
115+
{
116+
BinaryFormatter formatter = new BinaryFormatter();
117+
var deserializedClassMapping = (RootClass)formatter.Deserialize(configMemoryStream);
118+
119+
AssertDeserializedMappingClasses(deserializedClassMapping);
120+
}
121+
}
122+
}
123+
}

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@
712712
<Compile Include="NHSpecificTest\NH3459\Order.cs" />
713713
<Compile Include="NHSpecificTest\NH2692\Fixture.cs" />
714714
<Compile Include="NHSpecificTest\NH2692\Model.cs" />
715+
<Compile Include="NHSpecificTest\NH3383\FixtureByCode.cs" />
715716
<Compile Include="NHSpecificTest\NH2772\Model.cs" />
716717
<Compile Include="NHSpecificTest\NH2772\Fixture.cs" />
717718
<Compile Include="NHSpecificTest\NH3058\DomainClass.cs" />

src/NHibernate/Engine/CascadeStyle.cs

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Runtime.Serialization;
4+
using System.Security;
5+
using System.Security.Permissions;
36
using NHibernate.Util;
47

58
namespace NHibernate.Engine
@@ -77,12 +80,53 @@ public static CascadeStyle GetCascadeStyle(string cascade)
7780
return style;
7881
}
7982

83+
private static void RunTypeConstructor()
84+
{
85+
// No code needed.
86+
}
87+
8088
#endregion
8189

8290
#region The CascadeStyle implementations
8391

8492
[Serializable]
85-
private class AllDeleteOrphanCascadeStyle : CascadeStyle
93+
private abstract class SingletonCascadeStyle<TConcrete> : CascadeStyle, ISerializable
94+
where TConcrete : class, new()
95+
{
96+
public static readonly TConcrete Instance = new TConcrete();
97+
98+
#if NET_4_0
99+
[SecurityCritical]
100+
#else
101+
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
102+
#endif
103+
public void GetObjectData(SerializationInfo info, StreamingContext context)
104+
{
105+
// Don't serialize the real object, that could cause multiple objects of the same
106+
// singleton type after deserialization.
107+
info.SetType(typeof(CascadeStyleReference));
108+
}
109+
110+
[Serializable]
111+
private sealed class CascadeStyleReference : IObjectReference
112+
{
113+
#if NET_4_0
114+
[SecurityCritical]
115+
#endif
116+
public Object GetRealObject(StreamingContext context)
117+
{
118+
// First make sure that the type constructor of the base class was already executed.
119+
// If this isn't the case 'Instance' can't be constructed and will stay null.
120+
CascadeStyle.RunTypeConstructor();
121+
122+
// Return the singleton instance of this CascadeStyle.
123+
return Instance;
124+
}
125+
}
126+
}
127+
128+
[Serializable]
129+
private class AllDeleteOrphanCascadeStyle : SingletonCascadeStyle<AllDeleteOrphanCascadeStyle>
86130
{
87131
public override bool DoCascade(CascadingAction action)
88132
{
@@ -95,7 +139,7 @@ public override bool HasOrphanDelete
95139
}
96140

97141
[Serializable]
98-
private class AllCascadeStyle : CascadeStyle
142+
private class AllCascadeStyle : SingletonCascadeStyle<AllCascadeStyle>
99143
{
100144
public override bool DoCascade(CascadingAction action)
101145
{
@@ -104,7 +148,7 @@ public override bool DoCascade(CascadingAction action)
104148
}
105149

106150
[Serializable]
107-
private class UpdateCascadeStyle : CascadeStyle
151+
private class UpdateCascadeStyle : SingletonCascadeStyle<UpdateCascadeStyle>
108152
{
109153
public override bool DoCascade(CascadingAction action)
110154
{
@@ -113,7 +157,7 @@ public override bool DoCascade(CascadingAction action)
113157
}
114158

115159
[Serializable]
116-
private class LockCascadeStyle : CascadeStyle
160+
private class LockCascadeStyle : SingletonCascadeStyle<LockCascadeStyle>
117161
{
118162
public override bool DoCascade(CascadingAction action)
119163
{
@@ -122,7 +166,7 @@ public override bool DoCascade(CascadingAction action)
122166
}
123167

124168
[Serializable]
125-
private class RefreshCascadeStyle : CascadeStyle
169+
private class RefreshCascadeStyle : SingletonCascadeStyle<RefreshCascadeStyle>
126170
{
127171
public override bool DoCascade(CascadingAction action)
128172
{
@@ -131,7 +175,7 @@ public override bool DoCascade(CascadingAction action)
131175
}
132176

133177
[Serializable]
134-
private class EvictCascadeStyle : CascadeStyle
178+
private class EvictCascadeStyle : SingletonCascadeStyle<EvictCascadeStyle>
135179
{
136180
public override bool DoCascade(CascadingAction action)
137181
{
@@ -140,7 +184,7 @@ public override bool DoCascade(CascadingAction action)
140184
}
141185

142186
[Serializable]
143-
private class ReplicateCascadeStyle : CascadeStyle
187+
private class ReplicateCascadeStyle : SingletonCascadeStyle<ReplicateCascadeStyle>
144188
{
145189
public override bool DoCascade(CascadingAction action)
146190
{
@@ -149,7 +193,7 @@ public override bool DoCascade(CascadingAction action)
149193
}
150194

151195
[Serializable]
152-
private class MergeCascadeStyle : CascadeStyle
196+
private class MergeCascadeStyle : SingletonCascadeStyle<MergeCascadeStyle>
153197
{
154198
public override bool DoCascade(CascadingAction action)
155199
{
@@ -158,7 +202,7 @@ public override bool DoCascade(CascadingAction action)
158202
}
159203

160204
[Serializable]
161-
private class PersistCascadeStyle : CascadeStyle
205+
private class PersistCascadeStyle : SingletonCascadeStyle<PersistCascadeStyle>
162206
{
163207
public override bool DoCascade(CascadingAction action)
164208
{
@@ -167,7 +211,7 @@ public override bool DoCascade(CascadingAction action)
167211
}
168212

169213
[Serializable]
170-
private class DeleteCascadeStyle : CascadeStyle
214+
private class DeleteCascadeStyle : SingletonCascadeStyle<DeleteCascadeStyle>
171215
{
172216
public override bool DoCascade(CascadingAction action)
173217
{
@@ -176,7 +220,7 @@ public override bool DoCascade(CascadingAction action)
176220
}
177221

178222
[Serializable]
179-
private class DeleteOrphanCascadeStyle : CascadeStyle
223+
private class DeleteOrphanCascadeStyle : SingletonCascadeStyle<DeleteOrphanCascadeStyle>
180224
{
181225
public override bool DoCascade(CascadingAction action)
182226
{
@@ -195,7 +239,7 @@ public override bool HasOrphanDelete
195239
}
196240

197241
[Serializable]
198-
private class NoneCascadeStyle : CascadeStyle
242+
private class NoneCascadeStyle : SingletonCascadeStyle<NoneCascadeStyle>
199243
{
200244
public override bool DoCascade(CascadingAction action)
201245
{
@@ -251,40 +295,40 @@ public override string ToString()
251295
}
252296

253297
/// <summary> save / delete / update / evict / lock / replicate / merge / persist + delete orphans</summary>
254-
public static readonly CascadeStyle AllDeleteOrphan = new AllDeleteOrphanCascadeStyle();
298+
public static readonly CascadeStyle AllDeleteOrphan = AllDeleteOrphanCascadeStyle.Instance;
255299

256300
/// <summary> save / delete / update / evict / lock / replicate / merge / persist</summary>
257-
public static readonly CascadeStyle All = new AllCascadeStyle();
301+
public static readonly CascadeStyle All = AllCascadeStyle.Instance;
258302

259303
/// <summary> save / update</summary>
260-
public static readonly CascadeStyle Update = new UpdateCascadeStyle();
304+
public static readonly CascadeStyle Update = UpdateCascadeStyle.Instance;
261305

262306
/// <summary> lock</summary>
263-
public static readonly CascadeStyle Lock = new LockCascadeStyle();
307+
public static readonly CascadeStyle Lock = LockCascadeStyle.Instance;
264308

265309
/// <summary> refresh</summary>
266-
public static readonly CascadeStyle Refresh = new RefreshCascadeStyle();
310+
public static readonly CascadeStyle Refresh = RefreshCascadeStyle.Instance;
267311

268312
/// <summary> evict</summary>
269-
public static readonly CascadeStyle Evict = new EvictCascadeStyle();
313+
public static readonly CascadeStyle Evict = EvictCascadeStyle.Instance;
270314

271315
/// <summary> replicate</summary>
272-
public static readonly CascadeStyle Replicate = new ReplicateCascadeStyle();
316+
public static readonly CascadeStyle Replicate = ReplicateCascadeStyle.Instance;
273317

274318
/// <summary> merge</summary>
275-
public static readonly CascadeStyle Merge = new MergeCascadeStyle();
319+
public static readonly CascadeStyle Merge = MergeCascadeStyle.Instance;
276320

277321
/// <summary> create</summary>
278-
public static readonly CascadeStyle Persist = new PersistCascadeStyle();
322+
public static readonly CascadeStyle Persist = PersistCascadeStyle.Instance;
279323

280324
/// <summary> delete</summary>
281-
public static readonly CascadeStyle Delete = new DeleteCascadeStyle();
325+
public static readonly CascadeStyle Delete = DeleteCascadeStyle.Instance;
282326

283327
/// <summary> delete + delete orphans</summary>
284-
public static readonly CascadeStyle DeleteOrphan = new DeleteOrphanCascadeStyle();
328+
public static readonly CascadeStyle DeleteOrphan = DeleteOrphanCascadeStyle.Instance;
285329

286330
/// <summary> no cascades</summary>
287-
public static readonly CascadeStyle None = new NoneCascadeStyle();
331+
public static readonly CascadeStyle None = NoneCascadeStyle.Instance;
288332

289333
#endregion
290334
}

0 commit comments

Comments
 (0)