-
Notifications
You must be signed in to change notification settings - Fork 936
Fix transient detection for proxy #2045
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
Conversation
The change suspected in #2043 dates back to 5.0. Is this a regression of 5.0? |
Yes. By adding call to method So in fix I've added support for proxies (1st place as shortcut to |
This one will have to be back-ported down to 5.0. |
@@ -114,6 +114,11 @@ public object Instantiate(object id) | |||
|
|||
public object GetIdentifier(object entity) | |||
{ | |||
if (entity is INHibernateProxy proxy) |
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.
In other places the caller checks for proxy before calling this method.
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.
Indeed, in NHibernate.Type.EntityType
, the checks done there are no more needed with this PR changes.
There are other cases where the proxy is unproxied before hand, but that looks to me as a requirement of the implemented features there rather than due to GetIdentifier
not supporting proxies.
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.
In other places the caller checks for proxy before calling this method.
And your point? You prefer to keep it that way? To me it looks better to support it here.
Or at least move this check to AbstractEntityPersister.GetIdentifier
.
If it's about clean up other places - I think we can't remove checks in other places in this PR to avoid some custom implementations relying on it. I can add 6.0 TODO here for clean up.
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 would prefer to keep this check outside of this method as in other places.
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.
So how about moving it to AbstractEntityPersister.GetIdentifier
? As I don't see why persister.GetIdentifier
shouldn't handle proxies.
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 am seeing more benefits in having the checks inside that method rather than outside. I do not really see why this method should not do the check itself. It does look very lightweight to me, so having the check done for cases where it can never be a proxy should not have a significant impact. What am I missing?
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.
@fredericDelaporte I agree with you. But if you are OK with my latest changes I think this part is out of scope of this PR as now IsTransient logic never calls GetIdentifier
for proxies
This comment has been minimized.
This comment has been minimized.
{ | ||
return Task.FromResult<bool?>(false); | ||
} | ||
if (session.PersistenceContext.IsEntryFor(entity)) |
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.
Actually as shows NH607 this check is wrong. PersistenceContext can contain entries for transient objects.
And hack was added to support wrong implementation of IsNotTransientSlow
:
nhibernate-core/src/NHibernate/Engine/ForeignKeys.cs
Lines 249 to 255 in ac39173
/***********************************************/ | |
// TODO NH verify the behavior of NH607 test | |
// these lines are only to pass test NH607 during PersistenceContext porting | |
// i'm not secure that NH607 is a test for a right behavior | |
EntityEntry entry = session.PersistenceContext.GetEntry(entity); | |
if (entry != null) | |
return entry.Id; |
So in this PR I'm going to move this check back to IsNotTransientSlow
and in new PR for 5.3 will try to remove it completly.
src/NHibernate/Engine/ForeignKeys.cs
Outdated
@@ -171,6 +171,9 @@ public static bool IsNotTransientSlow(string entityName, object entity, ISession | |||
/// </remarks> | |||
public static bool? IsTransientFast(string entityName, object entity, ISessionImplementor session) | |||
{ | |||
if (entity.IsProxy()) | |||
return false; |
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.
So, despite some hesitations on that subject, now proxies are considered as never transient.
What happens if we load a proxy of an entity with an assigned identifier, initialize it, delete it in session, flush, and save it again? In such case we would have a transient proxy, and it should be possible to save it, I guess. If that case does work with currently released NHibernate, then the logic from "not transient" should not be applied to "transient" too. (And we should consider removing it from the "not" case.)
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 scenario still works (both direct and cascace saving) - as entity is unproxied along the way to IsTransientFast
. I will commit tests for such cases later.
But from correctness standpoint you are right - my initial implementation is more correct. It correctly detects transient objects from any context. So I think I simply need to drop all my commits to the state you've approved initially. Do you agree? That means that GetIdentifier
fix is also required here.
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.
Your initial implementation has my preference.
I would prefer to keep this check outside of this method as in other places.
Is-it because this is a patch of 5.2.x? So you consider that this kind of (even small) "semantic" change should be avoided?
(I do not actually see it as a semantic change. I see it more like fixing a bug in GetIdentifier
, since its documentation does not state it does not support proxies.)
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.
Sorry, I did not have time to clarify my position on this subject. While this check is ok here, I think it should be performed outside of this method. First of all, AbstractEntityPersister
/AbstractEntityTuplizer
does not deal with any kind of instantiaded proxies currently (except a delegated method to instantiate one). Secondly, ISessionImplementor.GetEntityPersister(...)
is a very heavy method and really should be left as a last resort. So, check for the proxy should happen outside of the persister to avoid its lookup.
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.
ISessionImplementor.GetEntityPersister(...) is a very heavy method and really should be left as a last resort.
Why single dictionary lookup is considered heavy? And in our case it's persister.IsTransient
method that calls GetIdentifier
. So we really save nothing.
It's either put this check in persister.IsTransient
vs GetIdentifier
(persister or tupilizer). To me the deeper the better - to cover all possible future cases (so in tupilizer).
AbstractEntityPersister/AbstractEntityTuplizer
does not deal with any kind of instantiaded proxies currently
I take it you meant - non-instantiated. As I already tell EntityPersister must be able to work with such proxy. For now it's only for IsTransient
method.
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.
Why single dictionary lookup is considered heavy?
You are confusing it with another method that is of ISessionFactory
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.
Ok. It could be heavy for subclasses. But that wasn't really the point. My main point for proper IsTransient
detection we need to support proxies right inside persister. And it's better to do right in GetIdentifier
method
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've pushed changes to the point I think is the right fix. Feel free to adjust it your way
e038843
to
a1d631d
Compare
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.
What about this last way of handling proxies? It avoids having the persister dealing with proxies for now. We could still go back on that subject in 5.3, while doing this minimal fix for 5.2.x.
Fixes #2043 in v5.1.x
Fixes #2043 in v5.0.x
Fixes #2043