Skip to content

Commit 5456d85

Browse files
Revert #378 - NH-3480
The NH-3453 part is not reverted. The NH-3480 part was wrecking the many-to-many mapping with property ref.
1 parent 9ed4314 commit 5456d85

File tree

12 files changed

+140
-159
lines changed

12 files changed

+140
-159
lines changed

src/NHibernate/Async/Engine/Collections.cs

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -145,51 +145,41 @@ private static Task ProcessNeverReferencedCollectionAsync(IPersistentCollection
145145
/// <param name="entity">The owner of the collection. </param>
146146
/// <param name="session">The session.</param>
147147
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
148-
public static Task ProcessReachableCollectionAsync(IPersistentCollection collection, CollectionType type, object entity, ISessionImplementor session, CancellationToken cancellationToken)
148+
public static async Task ProcessReachableCollectionAsync(IPersistentCollection collection, CollectionType type, object entity, ISessionImplementor session, CancellationToken cancellationToken)
149149
{
150-
if (cancellationToken.IsCancellationRequested)
150+
cancellationToken.ThrowIfCancellationRequested();
151+
collection.Owner = entity;
152+
CollectionEntry ce = session.PersistenceContext.GetCollectionEntry(collection);
153+
154+
if (ce == null)
151155
{
152-
return Task.FromCanceled<object>(cancellationToken);
156+
// refer to comment in StatefulPersistenceContext.addCollection()
157+
throw new HibernateException(string.Format("Found two representations of same collection: {0}", type.Role));
153158
}
154-
try
155-
{
156-
collection.Owner = entity;
157-
CollectionEntry ce = session.PersistenceContext.GetCollectionEntry(collection);
158159

159-
if (ce == null)
160-
{
161-
// refer to comment in StatefulPersistenceContext.addCollection()
162-
return Task.FromException<object>(new HibernateException(string.Format("Found two representations of same collection: {0}", type.Role)));
163-
}
164-
165-
// The CollectionEntry.isReached() stuff is just to detect any silly users
166-
// who set up circular or shared references between/to collections.
167-
if (ce.IsReached)
168-
{
169-
// We've been here before
170-
return Task.FromException<object>(new HibernateException(string.Format("Found shared references to a collection: {0}", type.Role)));
171-
}
172-
ce.IsReached = true;
160+
// The CollectionEntry.isReached() stuff is just to detect any silly users
161+
// who set up circular or shared references between/to collections.
162+
if (ce.IsReached)
163+
{
164+
// We've been here before
165+
throw new HibernateException(string.Format("Found shared references to a collection: {0}", type.Role));
166+
}
167+
ce.IsReached = true;
173168

174-
ISessionFactoryImplementor factory = session.Factory;
175-
ICollectionPersister persister = factory.GetCollectionPersister(type.Role);
176-
ce.CurrentPersister = persister;
177-
ce.CurrentKey = type.GetKeyOfOwner(entity, session); //TODO: better to pass the id in as an argument?
169+
ISessionFactoryImplementor factory = session.Factory;
170+
ICollectionPersister persister = factory.GetCollectionPersister(type.Role);
171+
ce.CurrentPersister = persister;
172+
ce.CurrentKey = await (type.GetKeyOfOwnerAsync(entity, session, cancellationToken)).ConfigureAwait(false); //TODO: better to pass the id in as an argument?
178173

179-
if (log.IsDebugEnabled())
180-
{
181-
log.Debug("Collection found: {0}, was: {1}{2}",
174+
if (log.IsDebugEnabled())
175+
{
176+
log.Debug("Collection found: {0}, was: {1}{2}",
182177
MessageHelper.CollectionInfoString(persister, collection, ce.CurrentKey, session),
183178
MessageHelper.CollectionInfoString(ce.LoadedPersister, collection, ce.LoadedKey, session),
184179
(collection.WasInitialized ? " (initialized)" : " (uninitialized)"));
185-
}
186-
187-
return PrepareCollectionForUpdateAsync(collection, ce, factory, cancellationToken);
188-
}
189-
catch (System.Exception ex)
190-
{
191-
return Task.FromException<object>(ex);
192180
}
181+
182+
await (PrepareCollectionForUpdateAsync(collection, ce, factory, cancellationToken)).ConfigureAwait(false);
193183
}
194184

195185
private static Task PrepareCollectionForUpdateAsync(IPersistentCollection collection, CollectionEntry entry, ISessionFactoryImplementor factory, CancellationToken cancellationToken)

src/NHibernate/Async/Loader/Loader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ private async Task ReadCollectionElementsAsync(object[] row, DbDataReader result
238238
}
239239
else
240240
{
241-
key = collectionPersister.CollectionType.GetKeyOfOwner(owner, session);
241+
key = await (collectionPersister.CollectionType.GetKeyOfOwnerAsync(owner, session, cancellationToken)).ConfigureAwait(false);
242242
//TODO: old version did not require hashmap lookup:
243243
//keys[collectionOwner].getIdentifier()
244244
}

src/NHibernate/Async/Type/CollectionType.cs

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -81,33 +81,23 @@ public override Task NullSafeSetAsync(DbCommand cmd, object value, int index, IS
8181
}
8282
}
8383

84-
public override Task<object> DisassembleAsync(object value, ISessionImplementor session, object owner, CancellationToken cancellationToken)
84+
public override async Task<object> DisassembleAsync(object value, ISessionImplementor session, object owner, CancellationToken cancellationToken)
8585
{
86-
if (cancellationToken.IsCancellationRequested)
87-
{
88-
return Task.FromCanceled<object>(cancellationToken);
89-
}
90-
try
91-
{
92-
//remember the uk value
86+
cancellationToken.ThrowIfCancellationRequested();
87+
//remember the uk value
9388

94-
//This solution would allow us to eliminate the owner arg to disassemble(), but
95-
//what if the collection was null, and then later had elements added? seems unsafe
96-
//session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();
89+
//This solution would allow us to eliminate the owner arg to disassemble(), but
90+
//what if the collection was null, and then later had elements added? seems unsafe
91+
//session.getPersistenceContext().getCollectionEntry( (PersistentCollection) value ).getKey();
9792

98-
object key = GetKeyOfOwner(owner, session);
99-
if (key == null)
100-
{
101-
return Task.FromResult<object>(null);
102-
}
103-
else
104-
{
105-
return GetPersister(session).KeyType.DisassembleAsync(key, session, owner, cancellationToken);
106-
}
93+
object key = await (GetKeyOfOwnerAsync(owner, session, cancellationToken)).ConfigureAwait(false);
94+
if (key == null)
95+
{
96+
return null;
10797
}
108-
catch (Exception ex)
98+
else
10999
{
110-
return Task.FromException<object>(ex);
100+
return await (GetPersister(session).KeyType.DisassembleAsync(key, session, owner, cancellationToken)).ConfigureAwait(false);
111101
}
112102
}
113103

@@ -152,20 +142,10 @@ public override Task<object> HydrateAsync(DbDataReader rs, string[] name, ISessi
152142
}
153143
}
154144

155-
public override Task<object> ResolveIdentifierAsync(object key, ISessionImplementor session, object owner, CancellationToken cancellationToken)
145+
public override async Task<object> ResolveIdentifierAsync(object key, ISessionImplementor session, object owner, CancellationToken cancellationToken)
156146
{
157-
if (cancellationToken.IsCancellationRequested)
158-
{
159-
return Task.FromCanceled<object>(cancellationToken);
160-
}
161-
try
162-
{
163-
return ResolveKeyAsync(GetKeyOfOwner(owner, session), session, owner, cancellationToken);
164-
}
165-
catch (Exception ex)
166-
{
167-
return Task.FromException<object>(ex);
168-
}
147+
cancellationToken.ThrowIfCancellationRequested();
148+
return await (ResolveKeyAsync(await (GetKeyOfOwnerAsync(owner, session, cancellationToken)).ConfigureAwait(false), session, owner, cancellationToken)).ConfigureAwait(false);
169149
}
170150

171151
private Task<object> ResolveKeyAsync(object key, ISessionImplementor session, object owner, CancellationToken cancellationToken)
@@ -310,5 +290,42 @@ public override Task<bool> IsModifiedAsync(object oldHydratedState, object curre
310290
return Task.FromException<bool>(ex);
311291
}
312292
}
293+
294+
/// <summary>
295+
/// Get the key value from the owning entity instance. It is usually the identifier, but it might be some
296+
/// other unique key, in the case of a property-ref.
297+
/// </summary>
298+
public async Task<object> GetKeyOfOwnerAsync(object owner, ISessionImplementor session, CancellationToken cancellationToken)
299+
{
300+
cancellationToken.ThrowIfCancellationRequested();
301+
var entityEntry = session.PersistenceContext.GetEntry(owner);
302+
if (entityEntry == null)
303+
{
304+
// This just handles a particular case of component
305+
// projection, perhaps get rid of it and throw an exception
306+
return null;
307+
}
308+
309+
if (foreignKeyPropertyName == null)
310+
{
311+
return entityEntry.Id;
312+
}
313+
314+
// TODO: at the point where we are resolving collection references, we don't
315+
// know if the uk value has been resolved (depends if it was earlier or
316+
// later in the mapping document) - now, we could try and use e.getStatus()
317+
// to decide to semiResolve(), trouble is that initializeEntity() reuses
318+
// the same array for resolved and hydrated values
319+
var id = entityEntry.LoadedState != null
320+
? entityEntry.GetLoadedValue(foreignKeyPropertyName)
321+
: entityEntry.Persister.GetPropertyValue(owner, foreignKeyPropertyName);
322+
// NOTE VERY HACKISH WORKAROUND!!
323+
var keyType = GetPersister(session).KeyType;
324+
if (!keyType.ReturnedClass.IsInstanceOfType(id))
325+
{
326+
id = await (keyType.SemiResolveAsync(entityEntry.GetLoadedValue(foreignKeyPropertyName), session, owner, cancellationToken)).ConfigureAwait(false);
327+
}
328+
return id;
329+
}
313330
}
314331
}

src/NHibernate/Engine/JoinHelper.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public static string[] GetRHSColumnNames(IAssociationType type, ISessionFactoryI
2727
IJoinable joinable = type.GetAssociatedJoinable(factory);
2828
if (uniqueKeyPropertyName == null)
2929
{
30-
return joinable.JoinColumnNames;
30+
return joinable.KeyColumnNames;
3131
}
3232
else
3333
{
@@ -208,4 +208,4 @@ public override string GetTableName(IAssociationType type)
208208
#endregion
209209
}
210210

211-
}
211+
}

src/NHibernate/Loader/Collection/OneToManyJoinWalker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ private void InitStatementString(IOuterJoinLoadable elementPersister, string ali
5858
int collectionJoins = CountCollectionPersisters(associations) + 1;
5959
CollectionSuffixes = BasicLoader.GenerateSuffixes(joins + 1, collectionJoins);
6060

61-
SqlStringBuilder whereString = WhereString(oneToManyPersister.GenerateTableAliasForKeyColumns(alias), oneToManyPersister.KeyColumnNames, subquery, batchSize);
61+
SqlStringBuilder whereString = WhereString(alias, oneToManyPersister.KeyColumnNames, subquery, batchSize);
6262
string filter = oneToManyPersister.FilterFragment(alias, EnabledFilters);
6363
whereString.Insert(0, StringHelper.MoveAndToBeginning(filter));
6464

@@ -71,7 +71,7 @@ private void InitStatementString(IOuterJoinLoadable elementPersister, string ali
7171
#pragma warning restore 618
7272
SelectString(associations))
7373
.SetFromClause(
74-
elementPersister.FromTableFragment(alias) + oneToManyPersister.FromJoinFragment(alias, true, true))
74+
elementPersister.FromTableFragment(alias) + elementPersister.FromJoinFragment(alias, true, true))
7575
.SetWhereClause(whereString.ToSqlString())
7676
.SetOuterJoins(ojf.ToFromFragmentString,
7777
ojf.ToWhereFragmentString + elementPersister.WhereJoinFragment(alias, true, true));

src/NHibernate/Persister/Collection/AbstractCollectionPersister.cs

Lines changed: 14 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ public abstract partial class AbstractCollectionPersister : ICollectionMetadata,
8686
private readonly string[] keyColumnAliases;
8787
private readonly string identifierColumnName;
8888
private readonly string identifierColumnAlias;
89-
private readonly string[] joinColumnNames;
9089

9190
#endregion
9291

@@ -226,45 +225,16 @@ public AbstractCollectionPersister(Mapping.Collection collection, ICacheConcurre
226225

227226
isVersioned = collection.IsOptimisticLocked;
228227

229-
if (collection.CollectionType.UseLHSPrimaryKey)
228+
keyType = collection.Key.Type;
229+
int keySpan = collection.Key.ColumnSpan;
230+
keyColumnNames = new string[keySpan];
231+
keyColumnAliases = new string[keySpan];
232+
int k = 0;
233+
foreach (Column col in collection.Key.ColumnIterator)
230234
{
231-
keyType = collection.Key.Type;
232-
int keySpan = collection.Key.ColumnSpan;
233-
keyColumnNames = new string[keySpan];
234-
keyColumnAliases = new string[keySpan];
235-
int k = 0;
236-
foreach (Column col in collection.Key.ColumnIterator)
237-
{
238-
keyColumnNames[k] = col.GetQuotedName(dialect);
239-
keyColumnAliases[k] = col.GetAlias(dialect, table);
240-
k++;
241-
}
242-
joinColumnNames = keyColumnNames;
243-
}
244-
else
245-
{
246-
keyType = collection.Owner.Key.Type;
247-
int keySpan = collection.Owner.Key.ColumnSpan;
248-
keyColumnNames = new string[keySpan];
249-
keyColumnAliases = new string[keySpan];
250-
int k = 0;
251-
foreach (Column col in collection.Owner.Key.ColumnIterator)
252-
{
253-
keyColumnNames[k] = col.GetQuotedName(dialect);
254-
// Force the alias to be unique in case it conflicts with an alias in the entity
255-
// As per Column.GetAlias, we have 3 characters left for SelectFragment suffix and one for here.
256-
// Since suffixes are composed of digits and '_', and GetAlias is already suffixed, adding any other
257-
// letter will avoid collision.
258-
keyColumnAliases[k] = col.GetAlias(dialect) + "o";
259-
k++;
260-
}
261-
joinColumnNames = new string[collection.Key.ColumnSpan];
262-
k = 0;
263-
foreach (Column col in collection.Key.ColumnIterator)
264-
{
265-
joinColumnNames[k] = col.GetQuotedName(dialect);
266-
k++;
267-
}
235+
keyColumnNames[k] = col.GetQuotedName(dialect);
236+
keyColumnAliases[k] = col.GetAlias(dialect, table);
237+
k++;
268238
}
269239

270240
HashSet<string> distinctColumns = new HashSet<string>();
@@ -1889,9 +1859,11 @@ public string[] KeyColumnNames
18891859
get { return keyColumnNames; }
18901860
}
18911861

1862+
// Since v5.2
1863+
[Obsolete("Use KeyColumnNames instead")]
18921864
public string[] JoinColumnNames
18931865
{
1894-
get { return joinColumnNames; }
1866+
get { return keyColumnNames; }
18951867
}
18961868

18971869
protected string[] KeyColumnAliases
@@ -2071,6 +2043,8 @@ protected Dialect.Dialect Dialect
20712043
public abstract bool CascadeDeleteEnabled { get; }
20722044
public abstract bool IsOneToMany { get; }
20732045

2046+
// Since v5.2
2047+
[Obsolete("Use directly the alias parameter value instead")]
20742048
public virtual string GenerateTableAliasForKeyColumns(string alias)
20752049
{
20762050
return alias;

src/NHibernate/Persister/Collection/IQueryableCollection.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using NHibernate.Persister.Entity;
23

34
namespace NHibernate.Persister.Collection
@@ -81,11 +82,13 @@ public interface IQueryableCollection : IPropertyMapping, IJoinable, ICollection
8182
/// <returns>Appropriate order-by fragment or empty string.</returns>
8283
string GetManyToManyOrderByString(string alias);
8384

85+
// Obsolete since v5.2
8486
/// <summary>
8587
/// Generate the table alias to use for the collection's key columns
8688
/// </summary>
8789
/// <param name="alias">The alias for the target table</param>
8890
/// <returns>Appropriate table alias.</returns>
91+
[Obsolete("Use directly the alias parameter value instead")]
8992
string GenerateTableAliasForKeyColumns(string alias);
9093
}
91-
}
94+
}

0 commit comments

Comments
 (0)