Skip to content

Commit 3dc86f3

Browse files
Merge pull request #1711 from fredericDelaporte/ProxySerialization
Fix static proxy serialization
2 parents d613336 + f5e702c commit 3dc86f3

File tree

3 files changed

+124
-5
lines changed

3 files changed

+124
-5
lines changed

src/NHibernate.Test/StaticProxyTest/StaticProxyFactoryFixture.cs

Lines changed: 66 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,61 @@ 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+
TestsContext.AssumeSystemTypeIsSerializable();
41+
42+
var factory = new StaticProxyFactory();
43+
factory.PostInstantiate(typeof(SimpleTestClass).FullName, typeof(SimpleTestClass), new HashSet<System.Type> {typeof(INHibernateProxy)}, null, null, null);
44+
var proxy = factory.GetProxy(2, null);
45+
Assert.That(proxy, Is.Not.Null, "proxy");
46+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.False, "proxy already initialized after creation");
47+
Assert.That(proxy.HibernateLazyInitializer, Is.Not.Null, "HibernateLazyInitializer");
48+
49+
var impl = new SimpleTestClass { Id = 2 };
50+
proxy.HibernateLazyInitializer.SetImplementation(impl);
51+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.True, "proxy not initialized after setting implementation");
52+
53+
var serializer = new BinaryFormatter();
54+
object deserialized;
55+
using (var memoryStream = new MemoryStream())
56+
{
57+
serializer.Serialize(memoryStream, proxy);
58+
memoryStream.Seek(0L, SeekOrigin.Begin);
59+
deserialized = serializer.Deserialize(memoryStream);
60+
}
61+
Assert.That(deserialized, Is.Not.Null, "deserialized");
62+
Assert.That(deserialized, Is.InstanceOf<INHibernateProxy>());
63+
Assert.That(NHibernateUtil.IsInitialized(deserialized), Is.True, "proxy no more initialized after deserialization");
64+
Assert.That(deserialized, Is.InstanceOf<SimpleTestClass>());
65+
Assert.That(((SimpleTestClass) deserialized).Id, Is.EqualTo(2));
66+
}
67+
68+
[Test]
69+
public void NonInitializedProxyStaysNonInitializedAfterSerialization()
70+
{
71+
TestsContext.AssumeSystemTypeIsSerializable();
72+
73+
var factory = new StaticProxyFactory();
74+
factory.PostInstantiate(typeof(SimpleTestClass).FullName, typeof(SimpleTestClass), new HashSet<System.Type> {typeof(INHibernateProxy)}, null, null, null);
75+
var proxy = factory.GetProxy(2, null);
76+
Assert.That(proxy, Is.Not.Null, "proxy");
77+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.False, "proxy already initialized after creation");
78+
79+
var serializer = new BinaryFormatter();
80+
object deserialized;
81+
using (var memoryStream = new MemoryStream())
82+
{
83+
serializer.Serialize(memoryStream, proxy);
84+
Assert.That(NHibernateUtil.IsInitialized(proxy), Is.False, "proxy initialized after serialization");
85+
memoryStream.Seek(0L, SeekOrigin.Begin);
86+
deserialized = serializer.Deserialize(memoryStream);
87+
}
88+
Assert.That(deserialized, Is.Not.Null, "deserialized");
89+
Assert.That(deserialized, Is.InstanceOf<INHibernateProxy>());
90+
Assert.That(NHibernateUtil.IsInitialized(deserialized), Is.False, "proxy initialized after deserialization");
91+
}
2792
}
2893
}

src/NHibernate/Proxy/NHibernateProxyBuilder.cs

Lines changed: 36 additions & 3 deletions
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);
@@ -95,7 +96,7 @@ public TypeInfo CreateProxyType(System.Type baseType, IReadOnlyCollection<System
9596
var customAttributeBuilder = new CustomAttributeBuilder(serializableConstructor, Array.Empty<object>());
9697
typeBuilder.SetCustomAttribute(customAttributeBuilder);
9798

98-
ImplementDeserializationConstructor(typeBuilder);
99+
ImplementDeserializationConstructor(typeBuilder, parentType);
99100
ImplementGetObjectData(typeBuilder, proxyInfoField, lazyInitializerField);
100101

101102
var proxyType = typeBuilder.CreateTypeInfo();
@@ -168,13 +169,24 @@ private static void ImplementConstructor(TypeBuilder typeBuilder, System.Type pa
168169
IL.Emit(OpCodes.Ret);
169170
}
170171

171-
private static void ImplementDeserializationConstructor(TypeBuilder typeBuilder)
172+
private static void ImplementDeserializationConstructor(TypeBuilder typeBuilder, System.Type parentType)
172173
{
173174
var parameterTypes = new[] {typeof (SerializationInfo), typeof (StreamingContext)};
174175
var constructor = typeBuilder.DefineConstructor(constructorAttributes, CallingConventions.Standard, parameterTypes);
175176
constructor.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed);
176177

177178
var IL = constructor.GetILGenerator();
179+
180+
constructor.SetImplementationFlags(MethodImplAttributes.IL | MethodImplAttributes.Managed);
181+
182+
var baseConstructor = parentType.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, null, System.Type.EmptyTypes, null);
183+
// if there is no default constructor, or the default constructor is private/internal, call System.Object constructor
184+
// this works, but the generated assembly will fail PeVerify (cannot use in medium trust for example)
185+
if (baseConstructor == null || baseConstructor.IsPrivate || baseConstructor.IsAssembly)
186+
baseConstructor = ObjectConstructor;
187+
IL.Emit(OpCodes.Ldarg_0);
188+
IL.Emit(OpCodes.Call, baseConstructor);
189+
178190
//Everything is done in NHibernateProxyObjectReference, so just return data.
179191
IL.Emit(OpCodes.Ret);
180192
}
@@ -199,7 +211,12 @@ private static void ImplementGetObjectData(TypeBuilder typeBuilder, FieldInfo pr
199211
IL.Emit(OpCodes.Call, ReflectionCache.TypeMethods.GetTypeFromHandle);
200212
IL.Emit(OpCodes.Callvirt, SerializationInfoSetTypeMethod);
201213

202-
// (new NHibernateProxyObjectReference(this.__proxyInfo, this.__lazyInitializer.Identifier)).GetObjectData(info, context);
214+
// return
215+
// (new NHibernateProxyObjectReference(
216+
// this.__proxyInfo,
217+
// this.__lazyInitializer.Identifier),
218+
// this.__lazyInitializer.IsUninitialized ? null : this.__lazyInitializer.GetImplementation())
219+
// .GetObjectData(info, context);
203220
//this.__proxyInfo
204221
IL.Emit(OpCodes.Ldarg_0);
205222
IL.Emit(OpCodes.Ldfld, proxyInfoField);
@@ -209,11 +226,27 @@ private static void ImplementGetObjectData(TypeBuilder typeBuilder, FieldInfo pr
209226
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
210227
IL.Emit(OpCodes.Callvirt, LazyInitializerIdentifierProperty.GetMethod);
211228

229+
// this.__lazyInitializer.IsUninitialized ? null : this.__lazyInitializer.GetImplementation()
230+
var isUnitialized = IL.DefineLabel();
231+
var endIsUnitializedTernary = IL.DefineLabel();
232+
IL.Emit(OpCodes.Ldarg_0);
233+
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
234+
IL.Emit(OpCodes.Callvirt, LazyInitializerIsUninitializedProperty.GetMethod);
235+
IL.Emit(OpCodes.Brtrue, isUnitialized);
236+
IL.Emit(OpCodes.Ldarg_0);
237+
IL.Emit(OpCodes.Ldfld, lazyInitializerField);
238+
IL.Emit(OpCodes.Callvirt, LazyInitializerGetImplementationMethod);
239+
IL.Emit(OpCodes.Br, endIsUnitializedTernary);
240+
IL.MarkLabel(isUnitialized);
241+
IL.Emit(OpCodes.Ldnull);
242+
IL.MarkLabel(endIsUnitializedTernary);
243+
212244
var constructor = typeof(NHibernateProxyObjectReference).GetConstructor(
213245
new[]
214246
{
215247
typeof(NHibernateProxyFactoryInfo),
216248
typeof(object),
249+
typeof(object)
217250
});
218251
IL.Emit(OpCodes.Newobj, constructor);
219252

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)