Skip to content
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

New StaticProxyFactoryFactory #1451

Merged
merged 8 commits into from
Dec 13, 2017
Merged

Conversation

hazzik
Copy link
Member

@hazzik hazzik commented Nov 22, 2017

No description provided.

@hazzik hazzik changed the title Proxy refactoring [WIP] Proxy refactoring Nov 22, 2017
@hazzik hazzik force-pushed the proxy-refactoring branch 2 times, most recently from 46477d6 to 5822c8e Compare November 22, 2017 11:26
@hazzik
Copy link
Member Author

hazzik commented Nov 22, 2017

At the current state of the PR, performance improvement is 16.8% on the data from #1446

Benchmarks.zip

@@ -81,10 +81,6 @@ public virtual object Invoke(MethodInfo method, object[] args, object proxy)
{
return null;
}
else if ("get_HibernateLazyInitializer".Equals(methodName))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We, probably, need to revert this change if we want to support [legacy] proxy factories.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and it looks to me there is no easy way to obsolete this code path: I do not think we can know there if the property implementation is itself proxied or not.

Copy link
Member

@fredericDelaporte fredericDelaporte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not confortable yet about my understanding of those proxies inner working. I am not used to the emit IL stuff either. But I think it is a welcomed change.

var IL = getMethod.GetILGenerator();

// This is equivalent to:
// get { return (INHibernateProxy)__interceptor; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment should be // get { return (ILazyInitializer)__interceptor; }

@@ -81,10 +81,6 @@ public virtual object Invoke(MethodInfo method, object[] args, object proxy)
{
return null;
}
else if ("get_HibernateLazyInitializer".Equals(methodName))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, and it looks to me there is no easy way to obsolete this code path: I do not think we can know there if the property implementation is itself proxied or not.

@hazzik hazzik mentioned this pull request Nov 25, 2017
@hazzik
Copy link
Member Author

hazzik commented Nov 25, 2017

Ok, I've finally come up with what I want to achieve with this PR. As all method implementations are known at the compile time, it would much beneficial to generate "static" proxies. This means that there will be no interceptors involved. This will allow removing the unnecessary overhead of a heavy reflection manipulations. The public classes and interfaces from DynamicProxy namespace will remain unchanged but marked as obsoleted in 5.1 and removed in 6.0. This will allow clients who use custom proxy factories with parts of our implementations to migrate/update their changes. We will also need to update NHibernateProxyGenerator (NHPG) to use the new approach.

@hazzik
Copy link
Member Author

hazzik commented Dec 1, 2017

Finally was able to run a benchmark. With the current state of the PR memory usage has dropped to

NHibernate v5.0.0.0 (v5.0.3.0) : 228 951 KB (234 446 840 bytes) (more than half of 5.0.3)

And execution time has fell for about 30% (2s vs 2.6s on my laptop)

@hazzik hazzik force-pushed the proxy-refactoring branch from bae9e88 to eec3d10 Compare December 2, 2017 08:24
@hazzik hazzik changed the title [WIP] Proxy refactoring Static INHibernateProxy Dec 2, 2017
@hazzik hazzik changed the title Static INHibernateProxy Implement static INHibernateProxy Dec 2, 2017
@hazzik hazzik changed the title Implement static INHibernateProxy DefaultProxyFactory generates static proxies for entities Dec 2, 2017
@hazzik
Copy link
Member Author

hazzik commented Dec 2, 2017

Final number:

241 491 KB

var result = Activator.CreateInstance(proxyType);
var proxy = (IProxy) result;
proxy.Interceptor = new DefaultDynamicLazyFieldInterceptor();
return result;
Copy link
Member Author

@hazzik hazzik Dec 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes to this method need to be reverted for now.

@@ -10,8 +11,9 @@ namespace NHibernate.Proxy
/// <summary>
/// Convenient common implementation for ProxyFactory
/// </summary>
public abstract class AbstractProxyFactory: IProxyFactory
public abstract class AbstractProxyFactory: IProxyFactory, ISerializable
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The easiest (and I think the correct) way to serialize a proxy was to serialize its proxy factory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the ISerializable interface to a public non sealed type is a gray area breaking change in my opinion, because all descendant need then to implement the deserialization constructor (and may need to extend the get data method for including their own additional data).
It is not binary breaking though, so maybe it can be accepted for a minor.

We should at least flag this as a possible breaking change. (I have just created the label for #1461.) In release notes we would have to state that descendants of AbstractProxyFactory must now provide the deserialization constructor and need to override GetObjectData if they have additional state data to serialize.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now realize this point is a possible breaking change: static proxies are serialized without their entity state, even if they were initialized. They are always de-serialized in un-initialized state.

So, for proxies de-serialized by a session de-serialization, using them will always triggers a lazy load even if they were serialized while already being loaded. It looks to me dynamic proxies would not trigger a lazy load in that case.

For proxies directly serialized/de-serialized, with dynamic proxies their de-serialized state should be usable if they were initialized before serialization, while static proxies would be always un-initialized and un-usable because no more bound to a session.

Maybe a way to get the initializer _target instance should be added, for serializing it along the other proxy data, thus allowing to supply it back to the proxy on deserialization.

[SecurityCritical]
public object GetRealObject(StreamingContext context)
{
return _proxyFactory.GetProxy(_identifier, null);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null here is for ISessionImplementation. On deserialization, the PersistanceContext will re-associate the session with the proxy. The proxy does support having a null session.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the code previously was never calling this method with null as the session parameter (that seems to be the case), it may be a breaking changed for a custom IProxyFactory. The contract does not state anything explicit about this parameter nullability. Should we care for implementors who would not support receiving it null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it may be a breaking changed for a custom IProxyFactory

In this codepath there will be no custom IProxyFactory.

The LazyInitializer underneath does support accepting null.

So, answer is no.

@gliljas
Copy link
Member

gliljas commented Dec 3, 2017

@hazzik Well done! I will try to find some time to review it properly this week (I sing in a choir, and it's Christmas time, so like six rehearsals this week.. :) )

@@ -48,9 +49,9 @@ public void TypeInequality()
[Test]
public void InterfaceEquality()
{
var entry1 = new ProxyCacheEntry(typeof(Entity1), new[] { typeof(INHibernateProxy), typeof(IProxy) });
var entry1 = new ProxyCacheEntry(typeof(Entity1), new[] { typeof(INHibernateProxy), typeof(IFieldInterceptorAccessor) });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change will conflict with #1454.

As #1451 now uses completely different approach, I’m going to remove from there changes related to this PR.

Seems not fully done. I guess you want to merge #1454 then rebase this PR for doing the cleanup.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It’s not “not fully done” it’s simply “not done”. Yeah, going to rebase on #1454 then cleanup

using NHibernate.Proxy;
using NHibernate.Proxy.DynamicProxy;
using NUnit.Framework;

namespace NHibernate.Test.NHSpecificTest.NH3954
{
[TestFixture, Explicit("Demonstrates bug impact on cache, but which tests will fail is a bit unpredictable")]
[TestFixture, Explicit("Demonstrates bug impact on cache, but which tests will fail is a bit unpredictable"), Obsolete]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as previous comment, same for IProxyCache, ProxyCache, ProxyCacheEntry, ProxyFactory and some other places.

@@ -10,8 +11,9 @@ namespace NHibernate.Proxy
/// <summary>
/// Convenient common implementation for ProxyFactory
/// </summary>
public abstract class AbstractProxyFactory: IProxyFactory
public abstract class AbstractProxyFactory: IProxyFactory, ISerializable
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding the ISerializable interface to a public non sealed type is a gray area breaking change in my opinion, because all descendant need then to implement the deserialization constructor (and may need to extend the get data method for including their own additional data).
It is not binary breaking though, so maybe it can be accepted for a minor.

We should at least flag this as a possible breaking change. (I have just created the label for #1461.) In release notes we would have to state that descendants of AbstractProxyFactory must now provide the deserialization constructor and need to override GetObjectData if they have additional state data to serialize.

{
EntityName = (string) info.GetValue(nameof(EntityName), typeof(string));
PersistentClass = (System.Type) info.GetValue(nameof(PersistentClass), typeof(System.Type));
Interfaces = (System.Type[]) info.GetValue(nameof(Interfaces), typeof(System.Type[]));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will mandate an update of #1425 if merged before, or will need to be changed accordingly to #1425 otherwise.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think that proxies would even work in (all) netstandard as Reflection.Emit is a netstandard 1.x + platforms rather than a pure netstandard.

#1425 would not really help here as I’m seraializing a MethodInfo as well.

Copy link
Contributor

@ngbrown ngbrown Dec 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MethodInfo in covered in #1425. I didn't think NHibernate on .netstandard worked without things like DynamicProxies. The target isn't iOS (which requires static compilation), but the targets able to run .Net Core.

Update: Is this pull request going to provide a compile-time proxy generation ability?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: Is this pull request going to provide a compile-time proxy generation ability?

It looks to me as being the purpose of https://github.com/nhibernate/NHibernate.ProxyGenerators. I do not expect this to change. Moreover Alexander had mentioned this project will need an update after this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have plans to include proxy generators into the core (as a tool + msbuild targets) to provide support for compile time proxies for the platforms which do not support runtime proxies. But not in this PR, and, most likely, not in this milestone.

return (INHibernateProxy) proxyInstance;
var proxyBuilder = new NHibernateProxyBuilder(GetIdentifierMethod, SetIdentifierMethod, ComponentIdType, OverridesEquals);
var cacheEntry = new ProxyCacheEntry(IsClassProxy ? PersistentClass : typeof(object), Interfaces);
var proxyActivator = Cache.GetOrAdd(cacheEntry, pke => CreateProxyActivator(proxyBuilder, pke));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the proxyActivator is retrieved from the cache, the proxyBuilder has then be created for nothing. I think it should be inlined into the GetOrAdd() second argument lambda.

@@ -51,5 +68,16 @@ public virtual object GetFieldInterceptionProxy(object instanceToWrap)
{
throw new NotSupportedException();
}

public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing [SecurityCritical] attribute.

@@ -206,7 +189,7 @@ private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, Sys
return constructor;
}

private static void ImplementGetObjectData(System.Type baseType, System.Type[] baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField)
internal static void ImplementGetObjectData(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related subject but not introduced by this PR: I wonder if in the case of dynamic code we should also add that [SecurityCritical] attribute to GetObjectData. I am not finding much information on the subject.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should, but I don't want to touch the DynamicProxy namespace

var parameterTypes = new[] {typeof (SerializationInfo), typeof (StreamingContext)};

MethodBuilder methodBuilder =
typeBuilder.DefineMethod("GetObjectData", attributes, typeof (void), parameterTypes);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too, should we add SecurityCritical?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@@ -257,7 +240,7 @@ private static void ImplementGetObjectData(System.Type baseType, System.Type[] b
IL.Emit(OpCodes.Ret);
}

private static void DefineSerializationConstructor(TypeBuilder typeBuilder, FieldInfo interceptorField, ConstructorBuilder defaultConstructor)
internal static void DefineSerializationConstructor(TypeBuilder typeBuilder, FieldInfo interceptorField, ConstructorBuilder defaultConstructor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related subject but not introduced by this PR: Is the method body emitted here useful? Since it is supposed to be deserialized by ProxyObjectReference. It should be implemented the same way than NHibernateProxyBuilder.ImplementDeserializationConstructor I think.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to touch/fix/change DynamicProxy namespace other than for major bugs. It's working, so let it be this way. The namespace itself is going to be retired in 6.0

IL.Emit(OpCodes.Ldarg_1);
IL.Emit(OpCodes.Ldarg_2);

IL.Emit(OpCodes.Callvirt, typeof(ISerializable).GetMethod(nameof(ISerializable.GetObjectData)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have now two different ways of implementing the an IObjectReference with proxies, since the one for DynamicProxy.ProxyFactory does not delegate the GetObjectData to DynamicProxy.ProxyObjectReference. I prefer the pattern you have done for NHibernateProxyObjectReference, but then we should align ProxyObjectReference on the same pattern.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not going to bother with DynamicProxy namespace. Going to obsolete it after converting lazy properties proxies to the same approach.

@hazzik hazzik force-pushed the proxy-refactoring branch 4 times, most recently from 2575b33 to 8d4df90 Compare December 8, 2017 00:05
@nhibernate nhibernate deleted a comment from fredericDelaporte Dec 8, 2017
@nhibernate nhibernate deleted a comment from fredericDelaporte Dec 8, 2017
@hazzik
Copy link
Member Author

hazzik commented Dec 8, 2017

I've cleaned the PR addressing some of the review comments.

@hazzik
Copy link
Member Author

hazzik commented Dec 8, 2017

Now it's implemented as a completely new feature.

@hazzik hazzik force-pushed the proxy-refactoring branch from e899fdb to 6c0ec4c Compare December 8, 2017 11:09
- The factory generates high-efficient static proxies (no interceptor)
@hazzik hazzik force-pushed the proxy-refactoring branch from 6c0ec4c to c48beb8 Compare December 8, 2017 11:13
@hazzik hazzik changed the title DefaultProxyFactory generates static proxies for entities New StaticProxyFactoryFactory Dec 8, 2017
@fredericDelaporte
Copy link
Member

I am not forgetting it and was intending to look it again today anyway.

Copy link
Member

@fredericDelaporte fredericDelaporte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't DefaultProxyFactoryFactory, DefaultProxyFactory,DefaultLazyInitializer, BasicLazyInitializer be obsoleted?

{
public class StaticProxyFactoryFactory : IProxyFactoryFactory
{
internal static StaticProxyFactoryFactory Instance = new StaticProxyFactoryFactory();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For enforcing the singleton pattern, I would define a private default constructor.

Copy link
Member Author

@hazzik hazzik Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a small memory optimization. I don't want to enforce the singleton pattern here. And, in fact, we cannot enforce it, as StaticProxyFactoryFactory can be manually configured as a proxy factory factory and will require the public constructor to be creatable by reflection.

Copy link
Member

@fredericDelaporte fredericDelaporte Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I forgot to think about configuration.

@@ -21,11 +21,11 @@ public sealed class ProxyFactory
{
internal static readonly ConcurrentDictionary<ProxyCacheEntry, TypeInfo> _cache = new ConcurrentDictionary<ProxyCacheEntry, TypeInfo>();

private static readonly ConstructorInfo defaultBaseConstructor = typeof(object).GetConstructor(new System.Type[0]);
internal static readonly ConstructorInfo defaultBaseConstructor = typeof(object).GetConstructor(new System.Type[0]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since DynamicProxy is supposed to be removed in some future further proxy refactoring, I would tend to duplicate such a field in NHibernateProxyBuilder rather than sharing it.
Otherwise I would also consider putting it in ReflectionCache.

Same for setType field.

@@ -139,7 +139,7 @@ private IEnumerable<MethodInfo> GetProxiableMethods(System.Type type, IEnumerabl
.Distinct();
}

private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, System.Type parentType)
internal static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, System.Type parentType)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used outside of ProxyFactory, could stay private.

@@ -166,7 +166,7 @@ private static ConstructorBuilder DefineConstructor(TypeBuilder typeBuilder, Sys
return constructor;
}

private static void ImplementGetObjectData(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField)
internal static void ImplementGetObjectData(System.Type baseType, IReadOnlyCollection<System.Type> baseInterfaces, TypeBuilder typeBuilder, FieldInfo interceptorField)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used outside of ProxyFactory, could stay private.

@@ -217,7 +217,7 @@ private static void ImplementGetObjectData(System.Type baseType, IReadOnlyCollec
IL.Emit(OpCodes.Ret);
}

private static void DefineSerializationConstructor(TypeBuilder typeBuilder, FieldInfo interceptorField, ConstructorBuilder defaultConstructor)
internal static void DefineSerializationConstructor(TypeBuilder typeBuilder, FieldInfo interceptorField, ConstructorBuilder defaultConstructor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used outside of ProxyFactory, could stay private.

}
else if (_componentIdType != null && _componentIdType.IsMethodOf(method))
{
ImplementCallMethodOnEmbeddedComponentId(typeBuilder, method, lazyInitializerField);
Copy link
Member

@fredericDelaporte fredericDelaporte Dec 12, 2017

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 implementation of IAbstractComponentType.IsMethodOf yielding anything else than false, so this code path looks untested. I know this comes from BasicLazyInitializer logic, but was this part still relevant?

Could it be component id classes were supposed to be themselves proxified? But I do not see where this could happen. Otherwise is this something meant to allow some special method of the entity, supposed to just forward on the id component, to be called without initializing the proxy?

(On Hibernate's side, there is an implementation of IAbstractComponentType.IsMethodOf yielding something else than false (indeed componentTuplizer.isMethodOf(method)), on EmbeddedComponentType. But this override is lacking on NHibernate's side. And component tuplizer is lacking the method too on NHibernate's side. On Hibernate's side it has a non falsy implementation on PojoComponentTuplizer. But I have not checked in which case the method argument could actually belong to the component, and I think there are more things lacking for this feature on NHibernate's side.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is that, or how it's supposed to be used. I've copied this from DefaultProxyFactory. Do you think that this path needs to be removed?

Copy link
Member

@fredericDelaporte fredericDelaporte Dec 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With what I have seen on Hibernate's side, it now looks to me as something partially ported, causing it to very likely never have any usage. Fully ascertaining this would require a bit more digging on Hibernate's side to see in which cases it serves.
I am always for removing useless code, but since it does not cause any harm, it is also not really worth it to dig more for checking it can really be removed without any risk.

So if you are not sure about it either, it is going to stay for now.

IL.Emit(OpCodes.Ldarg_0);
IL.Emit(OpCodes.Call, baseConstructor);

IL.Emit(OpCodes.Ldarg_0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add before:

// __lazyInitializer == lazyInitializer;

IL.Emit(OpCodes.Ldarg_1);
IL.Emit(OpCodes.Stfld, lazyInitializerField);

IL.Emit(OpCodes.Ldarg_0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add before:

// __proxyInfo == proxyInfo;

IL.Emit(OpCodes.Call, ReflectionCache.TypeMethods.GetTypeFromHandle);
IL.Emit(OpCodes.Callvirt, ProxyFactory.setType);

//this.__proxyInfo
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add before:

// (new NHibernateProxyObjectReference(__proxyInfo, __lazyInitializer.Identifier)).GetObjectDataMethod(info, context);

/*
set
{
this.__lazyInitializer.Initialize();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed should be prepended with:

if (this.__lazyInitializer == null)
	return base.<Method> (args..)

Same for next two methods.

@hazzik
Copy link
Member Author

hazzik commented Dec 12, 2017

Shouldn't DefaultProxyFactoryFactory, DefaultProxyFactory,DefaultLazyInitializer, BasicLazyInitializer be obsoleted?

Maybe in a next PR.

@hazzik hazzik merged commit bc30eb4 into nhibernate:master Dec 13, 2017
@hazzik hazzik deleted the proxy-refactoring branch December 13, 2017 00:04
fredericDelaporte added a commit to fredericDelaporte/nhibernate-core that referenced this pull request Oct 24, 2018
Follow up to nhibernate#1451 for still testing NH-2622, NH-2628 and nhibernate#1389
fredericDelaporte added a commit that referenced this pull request Oct 25, 2018
Follow up to #1451 for still testing NH-2622, NH-2628 and #1389
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants