-
Notifications
You must be signed in to change notification settings - Fork 933
Lazy properties static proxy #1709
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
Lazy properties static proxy #1709
Conversation
@@ -196,6 +196,8 @@ | |||
name: LinqReadonlyTestsContext | |||
- conversion: Ignore | |||
name: MultiThreadRunner | |||
- conversion: Ignore | |||
name: PeVerifier |
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.
It was generating async tests for asynchronously calling the PEVerify. There is no point in testing that. Included in this commit because I have added tests using PeVerifier
.
@@ -1,8 +1,10 @@ | |||
using System; | |||
using System.Collections; | |||
using NHibernate.Proxy.DynamicProxy; | |||
|
|||
namespace NHibernate.Test.DynamicEntity |
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.
The dynamic entity tests are using NHibernate dynamic proxies as a general purpose proxy provider. But that is not the purpose of NHibernate to provide such a thing. Those tests will have to be rewritten with an independent proxy provider if they are to be kept after dropping dynamic proxies.
public static TypeInfo CreateProxyType(System.Type baseType) | ||
{ | ||
// Avoid having a suffix ending with "Proxy", for disambiguation with INHibernateProxy proxies | ||
var typeName = $"{baseType.Name}ProxyForFieldInterceptor"; |
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.
Previous implementation was causing field interceptor proxies to be build with same type and assembly name than INHibernateProxy
. Apparently this was not causing any issue, even when lazily loading an entity as a INHibernateProxy
loading its target implementation as a field interceptor proxy due to the entity having lazy properties.
Still it seems to me cleaner to avoid the confusion.
// be restored by lazy load. The field interceptor proxy cannot do that: it only knows how to load | ||
// lazy properties, and not the other ones. (Moreover, its session is not restored on persistence | ||
// context deserialization, contrary to NHibernateProxy. This causes deserialized proxies to only be | ||
// usable for accessing data already loaded before serialization.) |
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.
Those limitations are the same than those of dynamic proxies serialization of a field interceptor proxy.
@@ -24,7 +23,6 @@ internal class NHibernateProxyBuilder | |||
private static readonly PropertyInfo LazyInitializerIdentifierProperty = LazyInitializerType.GetProperty(nameof(ILazyInitializer.Identifier)); | |||
private static readonly MethodInfo LazyInitializerInitializeMethod = LazyInitializerType.GetMethod(nameof(ILazyInitializer.Initialize)); | |||
private static readonly MethodInfo LazyInitializerGetImplementationMethod = LazyInitializerType.GetMethod(nameof(ILazyInitializer.GetImplementation), System.Type.EmptyTypes); | |||
private static readonly IProxyAssemblyBuilder ProxyAssemblyBuilder = new DefaultProxyAssemblyBuilder(); |
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 not seen any interest in keeping the NHibernate.Proxy.DynamicProxy.IProxyAssemblyBuilder
pattern now that it was used only by non-injectable private members. So I have rewritten its features into ProxyBuilderHelper
, and obsoleted it.
internal static void EnableDynamicAssemblySaving(bool enable, string saveAssemblyPath) | ||
{ | ||
_saveAssembly = enable; | ||
_saveAssemblyPath = saveAssemblyPath; |
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.
DefaultProxyAssemblyBuilder
was enabling that with compilation directives, which was not very practical. Moreover, it was not handling the save path, which was further complicating using the feature with potentially more file system access right troubles.
var factory = new ProxyFactory(); | ||
var interceptor = new DefaultDynamicLazyFieldInterceptor(); | ||
return factory.CreateProxy(PersistentClass, interceptor, typeof(IFieldInterceptorAccessor)); | ||
var cacheEntry = new ProxyCacheEntry(IsClassProxy ? PersistentClass : typeof(object), System.Type.EmptyTypes); |
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.
IsClassProxy ? PersistentClass : typeof(object)
has to be reverted to PersistentClass
: the field interception proxy does not handle being an interface proxy. (I will add a test, this is lacking.)
(Optionally, it may be attempted here to handle being an interface proxy. But the INHibernateProxy
interface will have to be filtered out of Interfaces
. And the support would only be partial, as there will be no way of handling interface methods, but only properties. So I do not think it should be done.)
if (baseType.IsInterface) | ||
{ | ||
parentType = typeof(object); | ||
interfaces.Add(baseType); |
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.
This does indeed not make sense for a field interceptor. The baseType
can only be an interface for entities mapped as abstract. The field interceptor proxy should never be built for such case, it is built only on entity load from database. And anyway, it can not work in such case, since it uses its base implementation for storing its state.
I have addressed the two previous concerns, but I now look into changing even more the lazy property proxy implementation, for having it delegating its state like then lazy entity proxy. It would give two benefits: handling serialization the same way, and supporting the |
26f0af4
to
632d8a7
Compare
632d8a7
to
5695457
Compare
This would require too much changes. Delegating its state causes issues to field setters and getters (from So I have squashed the fixups, and I consider this PR as ready for reviewing. |
Checking again the dynamic proxy factory case, its serialization was ensuring the proxy type was rebuilt, which my implementation currently does not. Switching this PR to WIP again. Testing this is a bit inconvenient as it requires to stop the process for unloading dynamic assemblies before deserializing. I intend to add some Update: my implementation is broken (it fails deserializing), but there was no serialization test of lazy property proxies to demonstrate it. |
5695457
to
262f316
Compare
262f316
to
5d77746
Compare
Commits redone, for having a correct serialization from the first one. Serialization tests added. Now this PR should really be ready. |
About serialization, there is one "non-user-bug" case still unsupported: if the user supplies a serialization surrogate handling the entity type, it will be ignored by the proxy serialization. We do not have access to surrogates from the |
This is a follow up to #1451.
The new static proxy factory was delegating the lazy properties proxy implementation to the old dynamic proxies.
With this PR, it does implement its own field interceptor, with far less reflection.
The PR also obsoletes dynamic proxies.