Skip to content

Commit eae467d

Browse files
committed
Use a simpler CascadeStyle singleton implementation. It has fewer classes, and also avoids the complex issue with type initialization order.
1 parent f790b99 commit eae467d

File tree

2 files changed

+112
-59
lines changed

2 files changed

+112
-59
lines changed

src/NHibernate.Test/NHSpecificTest/NH3383/FixtureByCode.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,46 @@ protected override HbmMapping GetMappings()
1919
return mapper.CompileMappingForAllExplicitlyAddedEntities();
2020
}
2121

22+
23+
[Test]
24+
public void DeserializedCascadeStyleRefersToSameObject()
25+
{
26+
CascadeStyle deserializedCascadeStyle;
27+
28+
using (var configMemoryStream = new MemoryStream())
29+
{
30+
var formatter = new BinaryFormatter();
31+
formatter.Serialize(configMemoryStream, CascadeStyle.Evict);
32+
configMemoryStream.Position = 0;
33+
deserializedCascadeStyle = (CascadeStyle) formatter.Deserialize(configMemoryStream);
34+
}
35+
36+
Assert.That(deserializedCascadeStyle, Is.SameAs(CascadeStyle.Evict));
37+
}
38+
39+
40+
[Test]
41+
public void CanRoundTripSerializedMultipleCascadeStyle()
42+
{
43+
CascadeStyle startingCascadeStyle =
44+
new CascadeStyle.MultipleCascadeStyle(new[] {CascadeStyle.Delete, CascadeStyle.Lock});
45+
CascadeStyle deserializedCascadeStyle;
46+
47+
using (var configMemoryStream = new MemoryStream())
48+
{
49+
var formatter = new BinaryFormatter();
50+
formatter.Serialize(configMemoryStream, startingCascadeStyle);
51+
configMemoryStream.Position = 0;
52+
deserializedCascadeStyle = (CascadeStyle)formatter.Deserialize(configMemoryStream);
53+
}
54+
55+
Assert.That(deserializedCascadeStyle, Is.TypeOf<CascadeStyle.MultipleCascadeStyle>());
56+
Assert.That(deserializedCascadeStyle.ToString(),
57+
Is.EqualTo(
58+
"[NHibernate.Engine.CascadeStyle+DeleteCascadeStyle,NHibernate.Engine.CascadeStyle+LockCascadeStyle]"));
59+
}
60+
61+
2262
[Test]
2363
public void DeserializedPropertyMapping_RefersToSameCascadeStyle()
2464
{

src/NHibernate/Engine/CascadeStyle.cs

Lines changed: 72 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
using System.Collections.Generic;
33
using System.Runtime.Serialization;
44
using System.Security;
5-
using System.Security.Permissions;
65
using NHibernate.Util;
76

87
namespace NHibernate.Engine
98
{
109
/// <summary> A contract for defining the aspects of cascading various persistence actions. </summary>
1110
/// <seealso cref="CascadingAction"/>
1211
[Serializable]
13-
public abstract class CascadeStyle
12+
public abstract class CascadeStyle : ISerializable
1413
{
1514
/// <summary> package-protected constructor</summary>
1615
internal CascadeStyle() {}
@@ -29,6 +28,19 @@ static CascadeStyle()
2928
Styles["remove"] = Delete; // adds remove as a sort-of alias for delete...
3029
Styles["delete-orphan"] = DeleteOrphan;
3130
Styles["none"] = None;
31+
32+
AliasByStyle[All] = "all";
33+
AliasByStyle[AllDeleteOrphan] = "all-delete-orphan";
34+
AliasByStyle[Update] = "save-update";
35+
AliasByStyle[Persist] = "persist";
36+
AliasByStyle[Merge] = "merge";
37+
AliasByStyle[Lock] = "lock";
38+
AliasByStyle[Refresh] = "refresh";
39+
AliasByStyle[Replicate] = "replicate";
40+
AliasByStyle[Evict] = "evict";
41+
AliasByStyle[Delete] = "delete";
42+
AliasByStyle[DeleteOrphan] = "delete-orphan";
43+
AliasByStyle[None] = "none";
3244
}
3345

3446
#region The CascadeStyle contract
@@ -66,7 +78,8 @@ public virtual bool HasOrphanDelete
6678

6779
#region Static helper methods
6880

69-
internal static readonly Dictionary<string, CascadeStyle> Styles = new Dictionary<string, CascadeStyle>();
81+
private static readonly Dictionary<string, CascadeStyle> Styles = new Dictionary<string, CascadeStyle>();
82+
private static readonly Dictionary<CascadeStyle, string> AliasByStyle = new Dictionary<CascadeStyle, string>();
7083

7184
/// <summary> Factory method for obtaining named cascade styles </summary>
7285
/// <param name="cascade">The named cascade style name. </param>
@@ -80,53 +93,43 @@ public static CascadeStyle GetCascadeStyle(string cascade)
8093
return style;
8194
}
8295

83-
private static void RunTypeConstructor()
96+
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
8497
{
85-
// No code needed.
98+
string alias = AliasByStyle[this];
99+
info.SetType(typeof (CascadeStyleSingletonReference));
100+
info.AddValue("cascadestyle", alias, typeof (string));
86101
}
87102

88-
#endregion
89-
90-
#region The CascadeStyle implementations
91-
92103
[Serializable]
93-
private abstract class SingletonCascadeStyle<TConcrete> : CascadeStyle, ISerializable
94-
where TConcrete : class, new()
104+
private sealed class CascadeStyleSingletonReference : IObjectReference, ISerializable
95105
{
96-
public static readonly TConcrete Instance = new TConcrete();
106+
private readonly string _cascadeStyle;
97107

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)
108+
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
104109
{
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));
110+
throw new NotImplementedException("This class should not be serialized directly.");
108111
}
109112

110-
[Serializable]
111-
private sealed class CascadeStyleReference : IObjectReference
113+
private CascadeStyleSingletonReference(SerializationInfo info, StreamingContext context)
112114
{
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();
115+
_cascadeStyle = (string) info.GetValue("cascadestyle", typeof (string));
116+
}
121117

122-
// Return the singleton instance of this CascadeStyle.
123-
return Instance;
124-
}
118+
[SecurityCritical]
119+
Object IObjectReference.GetRealObject(StreamingContext context)
120+
{
121+
// Redirect to the singleton instance for the correct cascade style.
122+
return GetCascadeStyle(_cascadeStyle);
125123
}
126124
}
127125

126+
#endregion
127+
128+
#region The CascadeStyle implementations
129+
130+
128131
[Serializable]
129-
private class AllDeleteOrphanCascadeStyle : SingletonCascadeStyle<AllDeleteOrphanCascadeStyle>
132+
private class AllDeleteOrphanCascadeStyle : CascadeStyle
130133
{
131134
public override bool DoCascade(CascadingAction action)
132135
{
@@ -139,7 +142,7 @@ public override bool HasOrphanDelete
139142
}
140143

141144
[Serializable]
142-
private class AllCascadeStyle : SingletonCascadeStyle<AllCascadeStyle>
145+
private class AllCascadeStyle : CascadeStyle
143146
{
144147
public override bool DoCascade(CascadingAction action)
145148
{
@@ -148,7 +151,7 @@ public override bool DoCascade(CascadingAction action)
148151
}
149152

150153
[Serializable]
151-
private class UpdateCascadeStyle : SingletonCascadeStyle<UpdateCascadeStyle>
154+
private class UpdateCascadeStyle : CascadeStyle
152155
{
153156
public override bool DoCascade(CascadingAction action)
154157
{
@@ -157,7 +160,7 @@ public override bool DoCascade(CascadingAction action)
157160
}
158161

159162
[Serializable]
160-
private class LockCascadeStyle : SingletonCascadeStyle<LockCascadeStyle>
163+
private class LockCascadeStyle : CascadeStyle
161164
{
162165
public override bool DoCascade(CascadingAction action)
163166
{
@@ -166,7 +169,7 @@ public override bool DoCascade(CascadingAction action)
166169
}
167170

168171
[Serializable]
169-
private class RefreshCascadeStyle : SingletonCascadeStyle<RefreshCascadeStyle>
172+
private class RefreshCascadeStyle : CascadeStyle
170173
{
171174
public override bool DoCascade(CascadingAction action)
172175
{
@@ -175,7 +178,7 @@ public override bool DoCascade(CascadingAction action)
175178
}
176179

177180
[Serializable]
178-
private class EvictCascadeStyle : SingletonCascadeStyle<EvictCascadeStyle>
181+
private class EvictCascadeStyle : CascadeStyle
179182
{
180183
public override bool DoCascade(CascadingAction action)
181184
{
@@ -184,7 +187,7 @@ public override bool DoCascade(CascadingAction action)
184187
}
185188

186189
[Serializable]
187-
private class ReplicateCascadeStyle : SingletonCascadeStyle<ReplicateCascadeStyle>
190+
private class ReplicateCascadeStyle : CascadeStyle
188191
{
189192
public override bool DoCascade(CascadingAction action)
190193
{
@@ -193,7 +196,7 @@ public override bool DoCascade(CascadingAction action)
193196
}
194197

195198
[Serializable]
196-
private class MergeCascadeStyle : SingletonCascadeStyle<MergeCascadeStyle>
199+
private class MergeCascadeStyle : CascadeStyle
197200
{
198201
public override bool DoCascade(CascadingAction action)
199202
{
@@ -202,7 +205,7 @@ public override bool DoCascade(CascadingAction action)
202205
}
203206

204207
[Serializable]
205-
private class PersistCascadeStyle : SingletonCascadeStyle<PersistCascadeStyle>
208+
private class PersistCascadeStyle : CascadeStyle
206209
{
207210
public override bool DoCascade(CascadingAction action)
208211
{
@@ -211,7 +214,7 @@ public override bool DoCascade(CascadingAction action)
211214
}
212215

213216
[Serializable]
214-
private class DeleteCascadeStyle : SingletonCascadeStyle<DeleteCascadeStyle>
217+
private class DeleteCascadeStyle : CascadeStyle
215218
{
216219
public override bool DoCascade(CascadingAction action)
217220
{
@@ -220,7 +223,7 @@ public override bool DoCascade(CascadingAction action)
220223
}
221224

222225
[Serializable]
223-
private class DeleteOrphanCascadeStyle : SingletonCascadeStyle<DeleteOrphanCascadeStyle>
226+
private class DeleteOrphanCascadeStyle : CascadeStyle
224227
{
225228
public override bool DoCascade(CascadingAction action)
226229
{
@@ -239,7 +242,7 @@ public override bool HasOrphanDelete
239242
}
240243

241244
[Serializable]
242-
private class NoneCascadeStyle : SingletonCascadeStyle<NoneCascadeStyle>
245+
private class NoneCascadeStyle : CascadeStyle
243246
{
244247
public override bool DoCascade(CascadingAction action)
245248
{
@@ -248,14 +251,24 @@ public override bool DoCascade(CascadingAction action)
248251
}
249252

250253
[Serializable]
251-
public sealed class MultipleCascadeStyle : CascadeStyle
254+
public sealed class MultipleCascadeStyle : CascadeStyle, ISerializable
252255
{
253256
private readonly CascadeStyle[] styles;
254257
public MultipleCascadeStyle(CascadeStyle[] styles)
255258
{
256259
this.styles = styles;
257260
}
258261

262+
private MultipleCascadeStyle(SerializationInfo info, StreamingContext context)
263+
{
264+
styles = (CascadeStyle[])info.GetValue("styles", typeof(CascadeStyle[]));
265+
}
266+
267+
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
268+
{
269+
info.AddValue("styles", styles);
270+
}
271+
259272
public override bool DoCascade(CascadingAction action)
260273
{
261274
for (int i = 0; i < styles.Length; i++)
@@ -295,40 +308,40 @@ public override string ToString()
295308
}
296309

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

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

303316
/// <summary> save / update</summary>
304-
public static readonly CascadeStyle Update = UpdateCascadeStyle.Instance;
317+
public static readonly CascadeStyle Update = new UpdateCascadeStyle();
305318

306319
/// <summary> lock</summary>
307-
public static readonly CascadeStyle Lock = LockCascadeStyle.Instance;
320+
public static readonly CascadeStyle Lock = new LockCascadeStyle();
308321

309322
/// <summary> refresh</summary>
310-
public static readonly CascadeStyle Refresh = RefreshCascadeStyle.Instance;
323+
public static readonly CascadeStyle Refresh = new RefreshCascadeStyle();
311324

312325
/// <summary> evict</summary>
313-
public static readonly CascadeStyle Evict = EvictCascadeStyle.Instance;
326+
public static readonly CascadeStyle Evict = new EvictCascadeStyle();
314327

315328
/// <summary> replicate</summary>
316-
public static readonly CascadeStyle Replicate = ReplicateCascadeStyle.Instance;
329+
public static readonly CascadeStyle Replicate = new ReplicateCascadeStyle();
317330

318331
/// <summary> merge</summary>
319-
public static readonly CascadeStyle Merge = MergeCascadeStyle.Instance;
332+
public static readonly CascadeStyle Merge = new MergeCascadeStyle();
320333

321334
/// <summary> create</summary>
322-
public static readonly CascadeStyle Persist = PersistCascadeStyle.Instance;
335+
public static readonly CascadeStyle Persist = new PersistCascadeStyle();
323336

324337
/// <summary> delete</summary>
325-
public static readonly CascadeStyle Delete = DeleteCascadeStyle.Instance;
338+
public static readonly CascadeStyle Delete = new DeleteCascadeStyle();
326339

327340
/// <summary> delete + delete orphans</summary>
328-
public static readonly CascadeStyle DeleteOrphan = DeleteOrphanCascadeStyle.Instance;
341+
public static readonly CascadeStyle DeleteOrphan = new DeleteOrphanCascadeStyle();
329342

330343
/// <summary> no cascades</summary>
331-
public static readonly CascadeStyle None = NoneCascadeStyle.Instance;
344+
public static readonly CascadeStyle None = new NoneCascadeStyle();
332345

333346
#endregion
334347
}

0 commit comments

Comments
 (0)