Ok so:
- SortedSet uses SortedDictionary internally. SortedDictionary does
not use a Hashtable internally to store it's keys and values, using
instead a TreeBase. When the entity is inserted into a SortedSet, no
evaluation of GetHashCode() is made.
- HashedSet uses Hashtable internally. Hashtable will evaluate
GetHashCode() on any object placed into it.
Scenario: within user code at runtime, a transient collection has a
proxy added to it
- if the collection is a SortedSet, no proxy initialization takes
place
- if the collection is a HashedSet, the proxy is initialized and any
onward associations (entities/collections) are added to the session
Scenario: during flush
- SortedSet: PersistentSet.GetSnapshot() is called, placing the
contents in a Hashtable -> proxies are initialized and exception is
thrown
- HashedSet: PersistentSet.GetSnapshot() is called, placing the
contents in a Hashtable -> proxies are already initialized so no
exception is thrown
I think this is inconsistent. I can see two alternatives:
1) if I override GetSnapshot and change the use of Hashtable to
SortedDictionary, no exception is thrown
public override ICollection GetSnapshot(ICollectionPersister
persister)
{
EntityMode entityMode = Session.EntityMode;
//if (set==null) return new Set(session);
SortedDictionary<object, object> clonedSet = new
SortedDictionary<object, object>();
foreach (object current in set)
{
object copied = persister.ElementType.DeepCopy(current,
entityMode,
persister.Factory);
clonedSet[copied] = copied;
}
return clonedSet;
}
However, there are obviously performance implications in using a
SortedDictionary vs Hashtable as the size of the collection grows.
2) Call GetHashCode() when an element is added to a DictionarySet:
public override bool Add(T o)
{
if (InternalDictionary.ContainsKey(o))
{
return false;
}
else
{
//The object we are adding is just a placeholder. The thing we
are
//really concerned with is 'o', the key.
InternalDictionary.Add(o, PlaceholderObject);
o.GetHashCode();
return true;
}
}
This may modify behaviour of the applications currently using this
class.
Thoughts?
On Jun 4, 12:07 pm, Lee Henson <[email protected]> wrote:
> I've created a patch file for the current nh trunk, but I can't see
> anywhere to upload it to this list. In the meantime, it is available
> here:
>
> http://gist.github.com/123560
>
> The patch file includes caching of GetHashCode() values as you
> suggested, but it has no effect. The act of adding the Child in the
> Test() method doesn't appear to trigger to a call to it's GetHashCode
> () (which is what I presume you were expecting to fix the problem,
> because that would happen before the flush occurred).
>
> An alternative example where this would occur is if a user writes
> their own flush-related event handler and triggers a proxy
> initialization. Any situation in which a proxy is initialized within
> AbstractFlushingEventListener.PerformExecutions. Although in this
> situation I guess you could say "just don't do that".
>
> On Jun 3, 10:49 pm, Fabio Maulo <[email protected]> wrote:
>
> > here is the problem
>
> > unchecked
> > {
> > return (Age*397) ^ (Parent != null ? Parent.GetHashCode() : 0);}
>
> > The hashCode can't change during instance life time especially when you are
> > using it in a HashTable.
> > private int requestedHash?
> > public override int GetHashCode()
> > {
> > if(!requestedHash.HasValue)
> > {
> > unchecked
> > {
> > requestedHash = (Age*397) ^ (Parent != null ? Parent.GetHashCode() :
> > 0);
> > }}
>
> > return requestedHash;
>
> > }
>
> > 2009/6/3 Lee Henson <[email protected]>
>
> > > I have a test case that contains the following scenario:
>
> > > Parent has a custom generic set of Child which is empty
> > > Parent has an association to a MedicalRecord
> > > Doctor has an association to the same MedicalRecord
> > > MedicalRecord has an set of Disease which is empty
>
> > > In a new session:
>
> > > I load the Doctor, which has the side-effect of creating a proxy of
> > > MedicalRecord
> > > I call a method on my Parent.Child custom collection which adds a
> > > Child item
> > > I save the Parent
>
> > > Within the flush machinery, my Child gets placed in a Hashtable
> > > (PersistentSet.GetSnapshot()) which causes an evaluation of GetHashCode
> > > (). Since my Child.GetHashCode has a dependency on the
> > > Parent.GetHashCode it calls that too, and that in turn calls
> > > MedicalRecord.GetHashCode. At this point, because MedicalRecord
> > > already exists in the session as a proxy, it attempts to initialize
> > > the proxy. This causes the Diseases collection to be added to the
> > > session, and before you know it:
>
> > > AssertionFailure - collection was not processed by flush
>
> > > It might be a bit of a contrived example, but in my real model which
> > > is a tad more complicated I am seeing this error. Rather than paste
> > > all the code in this message, you can view the test case files/
> > > mappings here:
>
> > >http://github.com/leemhenson/nhibernate/commit/4728a32efa48dc836441d3...
>
> > > There are a couple of other jira issues logged in this area for java's
> > > hibernate:
>
> > >http://opensource.atlassian.com/projects/hibernate/browse/HHH-2763;js...
>
> > >http://opensource.atlassian.com/projects/hibernate/browse/HHH-3225;js...
> > >http://opensource.atlassian.com/projects/hibernate/browse/HSEARCH-178
>
> > > Thoughts?
>
> > > Cheers
> > > Lee
>
> > --
> > Fabio Maulo
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/nhusers?hl=en
-~----------~----~----~----~------~----~------~--~---