-
Notifications
You must be signed in to change notification settings - Fork 933
Fix static proxy serialization #1711
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix static proxy serialization #1711
Conversation
if (baseConstructor == null || baseConstructor.IsPrivate || baseConstructor.IsAssembly) | ||
baseConstructor = ObjectConstructor; | ||
IL.Emit(OpCodes.Ldarg_0); | ||
IL.Emit(OpCodes.Call, baseConstructor); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have checked PeVerify with following test added in PeVerifyFixture
:
[Test]
public void VerifyStaticProxy()
{
var proxyBuilderType = typeof(StaticProxyFactory).Assembly.GetType("NHibernate.Proxy.NHibernateProxyBuilder", true);
var proxyBuilder = proxyBuilderType.GetMethod("CreateProxyType");
Assert.That(proxyBuilder, Is.Not.Null, "Failed to find method CreateProxyType");
var proxyBuilderAssemblyBuilder = proxyBuilderType.GetField("ProxyAssemblyBuilder", BindingFlags.NonPublic | BindingFlags.Static);
Assert.That(proxyBuilderAssemblyBuilder, Is.Not.Null, "Failed to find assembly builder field");
var builderCtor = proxyBuilderType.GetConstructor(
new[] { typeof(MethodInfo), typeof(MethodInfo), typeof(IAbstractComponentType), typeof(bool) });
Assert.That(builderCtor, Is.Not.Null, "Failed to find builder ctor");
var builder = builderCtor.Invoke(new object[] { null, null, null, false });
var assemblyBuilder = new SavingProxyAssemblyBuilder(assemblyName);
var backupAssemblyBuilder = proxyBuilderAssemblyBuilder.GetValue(null);
proxyBuilderAssemblyBuilder.SetValue(null, assemblyBuilder);
try
{
proxyBuilder.Invoke(builder, new object[] { typeof(ClassWithPublicDefaultConstructor), new[] { typeof(INHibernateProxy) } });
}
finally
{
proxyBuilderAssemblyBuilder.SetValue(null, backupAssemblyBuilder);
}
new PeVerifier($"{assemblyName}.dll").AssertIsValid();
}
I have not added it in the PR as testing this would be much easier with #1709, without needs to depend on dynamic proxies test classes.
As I understand this could only happen if someone decides to serialise a proxy object directly as you do in the test cases? |
e41bfc5
to
6d16aa4
Compare
6d16aa4
to
f5e702c
Compare
Yes, the described proxy failure case can only happen in direct serialization cases. (Anyone still using remoting?) The performance loss happen in session serialization case. (Rebased for squashing third commit in first, and re-triggering TC builds which have timed-out.) |
In fact not, after further rechecking. I have changed the description about that point to:
And in case someone wonder if having the proxies serializing their target entity along with the persistence context serializing all entities and all proxies would cause the deserialized proxy to have a distinct instance from the one enlisted in the persistence context, this is not the cause. Binary serialization in .Net takes care of instances uniqueness, and restores objects which were holding references to a common instance in such a way that they still shares their common reference after deserialization. |
The static proxy serialization logic causes a regression compared to the previous dynamic proxy serialization: it loses the initialized state of the proxy, causing it to be always un-initialized after de-serialization.
This causes a performance loss for session serialization cases, since all proxies will lazily initialize if used, even if prior to serialization they were already initialized.This causes some strange behavior for session serialization cases, since all proxies will be restored flagged as un-initialized, even if prior to serialization they were already initialized. But those previously loaded will, on demand, initialize without causing a round-trip to the database, so there is no performance loss here. Still it would be better to keep a coherent state of the proxies in regard to the entities actually loaded in the session.This causes a failure in case of direct serialization of an initialized proxy, since after deserialization, if used, it will attempt to lazily initialize and it will fail, being moreover no more connected to a session. Previously, it would have still been initialized and it would not have tried to lazily initialize on use.
Additionally, the static proxy deserialization constructor implementation always fails PeVerify. This implementation is not really used since deserialization is delegated to an object reference, but still, better generate assemblies not failing PeVerify whenever possible.