Skip to content

Commit 047bcef

Browse files
Cease losing proxy initialized state on serialization
1 parent fb544b0 commit 047bcef

File tree

3 files changed

+107
-3
lines changed

3 files changed

+107
-3
lines changed

src/NHibernate.Test/StaticProxyTest/StaticProxyFactoryFixture.cs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
using System.Collections.Generic;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Runtime.Serialization.Formatters.Binary;
25
using NHibernate.Proxy;
36
using NUnit.Framework;
47

@@ -16,6 +19,12 @@ public class TestClass : ISomething
1619
public virtual int Id { get; set; }
1720
}
1821

22+
[Serializable]
23+
public class SimpleTestClass
24+
{
25+
public virtual int Id { get; set; }
26+
}
27+
1928
[Test]
2029
public void CanCreateProxyForClassWithInternalInterface()
2130
{
@@ -24,5 +33,57 @@ public void CanCreateProxyForClassWithInternalInterface()
2433
var proxy = factory.GetProxy(1, null);
2534
Assert.That(proxy, Is.Not.Null);
2635
}
36+
37+
[Test]
38+
public void InitializedProxyStaysInitializedAfterDeserialization()
39+
{
40+
var factory = new StaticProxyFactory();
41+
factory.PostInstantiate(typeof(SimpleTestClass).FullName, typeof(SimpleTestClass), new HashSet<System.Type> {typeof(INHibernateProxy)}, null, null, null);
42+
var proxy = factory.GetProxy(2, null);
43+
Assert.That(proxy, Is.Not.Null, "proxy");
44+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.False, "proxy already initialized after creation");
45+
Assert.That(proxy.HibernateLazyInitializer, Is.Not.Null, "HibernateLazyInitializer");
46+
47+
var impl = new SimpleTestClass { Id = 2 };
48+
proxy.HibernateLazyInitializer.SetImplementation(impl);
49+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.True, "proxy not initialized after setting implementation");
50+
51+
var serializer = new BinaryFormatter();
52+
object deserialized;
53+
using (var memoryStream = new MemoryStream())
54+
{
55+
serializer.Serialize(memoryStream, proxy);
56+
memoryStream.Seek(0L, SeekOrigin.Begin);
57+
deserialized = serializer.Deserialize(memoryStream);
58+
}
59+
Assert.That(deserialized, Is.Not.Null, "deserialized");
60+
Assert.That(deserialized, Is.InstanceOf<INHibernateProxy>());
61+
Assert.That(NHibernateUtil.IsInitialized(deserialized), Is.True, "proxy no more initialized after deserialization");
62+
Assert.That(deserialized, Is.InstanceOf<SimpleTestClass>());
63+
Assert.That(((SimpleTestClass) deserialized).Id, Is.EqualTo(2));
64+
}
65+
66+
[Test]
67+
public void NonInitializedProxyStaysNonInitializedAfterSerialization()
68+
{
69+
var factory = new StaticProxyFactory();
70+
factory.PostInstantiate(typeof(SimpleTestClass).FullName, typeof(SimpleTestClass), new HashSet<System.Type> {typeof(INHibernateProxy)}, null, null, null);
71+
var proxy = factory.GetProxy(2, null);
72+
Assert.That(proxy, Is.Not.Null, "proxy");
73+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.False, "proxy already initialized after creation");
74+
75+
var serializer = new BinaryFormatter();
76+
object deserialized;
77+
using (var memoryStream = new MemoryStream())
78+
{
79+
serializer.Serialize(memoryStream, proxy);
80+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.False, "proxy initialized after serialization");
81+
memoryStream.Seek(0L, SeekOrigin.Begin);
82+
deserialized = serializer.Deserialize(memoryStream);
83+
}
84+
Assert.That(deserialized, Is.Not.Null, "deserialized");
85+
Assert.That(deserialized, Is.InstanceOf<INHibernateProxy>());
86+
Assert.That(NHibernateUtil.IsInitialized(deserialized), Is.False, "proxy initialized after deserialization");
87+
}
2788
}
2889
}

src/NHibernate/Proxy/NHibernateProxyBuilder.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal class NHibernateProxyBuilder
2424
private static readonly PropertyInfo LazyInitializerIdentifierProperty = LazyInitializerType.GetProperty(nameof(ILazyInitializer.Identifier));
2525
private static readonly MethodInfo LazyInitializerInitializeMethod = LazyInitializerType.GetMethod(nameof(ILazyInitializer.Initialize));
2626
private static readonly MethodInfo LazyInitializerGetImplementationMethod = LazyInitializerType.GetMethod(nameof(ILazyInitializer.GetImplementation), System.Type.EmptyTypes);
27+
private static readonly PropertyInfo LazyInitializerIsUninitializedProperty = LazyInitializerType.GetProperty(nameof(ILazyInitializer.IsUninitialized));
2728
private static readonly IProxyAssemblyBuilder ProxyAssemblyBuilder = new DefaultProxyAssemblyBuilder();
2829

2930
private static readonly ConstructorInfo SecurityCriticalAttributeConstructor = typeof(SecurityCriticalAttribute).GetConstructor(System.Type.EmptyTypes);
@@ -199,7 +200,12 @@ private static void ImplementGetObjectData(TypeBuilder typeBuilder, FieldInfo pr
199200
IL.Emit(OpCodes.Call, ReflectionCache.TypeMethods.GetTypeFromHandle);
200201
IL.Emit(OpCodes.Callvirt, SerializationInfoSetTypeMethod);
201202

202-
// (new NHibernateProxyObjectReference(this.__proxyInfo, this.__lazyInitializer.Identifier)).GetObjectData(info, context);
203+
// return
204+
// (new NHibernateProxyObjectReference(
205+
// this.__proxyInfo,
206+
// this.__lazyInitializer.Identifier),
207+
// this.__lazyInitializer.IsUninitialized ? null : this.__lazyInitializer.GetImplementation())
208+
// .GetObjectData(info, context);
203209
//this.__proxyInfo
204210
IL.Emit(OpCodes.Ldarg_0);
205211
IL.Emit(OpCodes.Ldfld, proxyInfoField);
@@ -209,11 +215,27 @@ private static void ImplementGetObjectData(TypeBuilder typeBuilder, FieldInfo pr
209215
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
210216
IL.Emit(OpCodes.Callvirt, LazyInitializerIdentifierProperty.GetMethod);
211217

218+
// this.__lazyInitializer.IsUninitialized ? null : this.__lazyInitializer.GetImplementation()
219+
var isUnitialized = IL.DefineLabel();
220+
var endIsUnitializedTernary = IL.DefineLabel();
221+
IL.Emit(OpCodes.Ldarg_0);
222+
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
223+
IL.Emit(OpCodes.Callvirt, LazyInitializerIsUninitializedProperty.GetMethod);
224+
IL.Emit(OpCodes.Brtrue, isUnitialized);
225+
IL.Emit(OpCodes.Ldarg_0);
226+
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
227+
IL.Emit(OpCodes.Callvirt, LazyInitializerGetImplementationMethod);
228+
IL.Emit(OpCodes.Br, endIsUnitializedTernary);
229+
IL.MarkLabel(isUnitialized);
230+
IL.Emit(OpCodes.Ldnull);
231+
IL.MarkLabel(endIsUnitializedTernary);
232+
212233
var constructor = typeof(NHibernateProxyObjectReference).GetConstructor(
213234
new[]
214235
{
215236
typeof(NHibernateProxyFactoryInfo),
216237
typeof(object),
238+
typeof(object)
217239
});
218240
IL.Emit(OpCodes.Newobj, constructor);
219241

src/NHibernate/Proxy/NHibernateProxyObjectReference.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,51 @@ public sealed class NHibernateProxyObjectReference : IObjectReference, ISerializ
99
{
1010
private readonly NHibernateProxyFactoryInfo _proxyFactoryInfo;
1111
private readonly object _identifier;
12+
private readonly object _implementation;
1213

14+
[Obsolete("Use overload taking an implementation parameter")]
1315
public NHibernateProxyObjectReference(NHibernateProxyFactoryInfo proxyFactoryInfo, object identifier)
16+
: this (proxyFactoryInfo, identifier, null)
17+
{}
18+
19+
public NHibernateProxyObjectReference(NHibernateProxyFactoryInfo proxyFactoryInfo, object identifier, object implementation)
1420
{
1521
_proxyFactoryInfo = proxyFactoryInfo;
1622
_identifier = identifier;
23+
_implementation = implementation;
1724
}
1825

1926
private NHibernateProxyObjectReference(SerializationInfo info, StreamingContext context)
2027
{
2128
_proxyFactoryInfo = (NHibernateProxyFactoryInfo) info.GetValue(nameof(_proxyFactoryInfo), typeof(NHibernateProxyFactoryInfo));
2229
_identifier = info.GetValue(nameof(_identifier), typeof(object));
30+
// 6.0 TODO: simplify with info.GetValue(nameof(_implementation), typeof(object));
31+
foreach (var entry in info)
32+
{
33+
if (entry.Name == nameof(_implementation))
34+
{
35+
_implementation = entry.Value;
36+
}
37+
}
2338
}
2439

2540
[SecurityCritical]
2641
public object GetRealObject(StreamingContext context)
2742
{
28-
return _proxyFactoryInfo.CreateProxyFactory().GetProxy(_identifier, null);
43+
var proxy = _proxyFactoryInfo.CreateProxyFactory().GetProxy(_identifier, null);
44+
45+
if (_implementation != null)
46+
proxy.HibernateLazyInitializer.SetImplementation(_implementation);
47+
48+
return proxy;
2949
}
3050

3151
[SecurityCritical]
3252
public void GetObjectData(SerializationInfo info, StreamingContext context)
3353
{
3454
info.AddValue(nameof(_proxyFactoryInfo), _proxyFactoryInfo);
3555
info.AddValue(nameof(_identifier), _identifier);
56+
info.AddValue(nameof(_implementation), _implementation);
3657
}
3758
}
3859
}

0 commit comments

Comments
 (0)