Skip to content

Commit 4fb8f57

Browse files
fixup! Support IdBag in select id generator
Refactor the resulting code
1 parent 3fa9663 commit 4fb8f57

File tree

8 files changed

+282
-111
lines changed

8 files changed

+282
-111
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System;
12+
using System.Data.Common;
13+
using System.Linq;
14+
using NHibernate.Engine;
15+
using NHibernate.Id.Insert;
16+
using NHibernate.Persister.Collection;
17+
using NHibernate.Persister.Entity;
18+
using NHibernate.SqlCommand;
19+
using NHibernate.Type;
20+
21+
namespace NHibernate.Id
22+
{
23+
using System.Threading.Tasks;
24+
using System.Threading;
25+
26+
public partial interface ICompositeKeyPostInsertIdentityPersister
27+
{
28+
29+
/// <summary>
30+
/// Bind the parameter values of a SQL select command that performs a select based on an unique key.
31+
/// </summary>
32+
/// <param name="session">The current <see cref="ISession" />.</param>
33+
/// <param name="selectCommand">The command.</param>
34+
/// <param name="binder">The id insertion binder.</param>
35+
/// <param name="suppliedPropertyNames">The names of the properties which map to the column(s) to use
36+
/// in the select statement restriction. If supplied, they override the persister logic for determining
37+
/// them.</param>
38+
/// <param name="cancellationToken">A cancellation token that can be used to cancel the work</param>
39+
/// <exception cref="NotSupportedException">thrown if <paramref name="suppliedPropertyNames"/> are
40+
/// specified on a persister which does not allow a custom key.</exception>
41+
Task BindSelectByUniqueKeyAsync(
42+
ISessionImplementor session,
43+
DbCommand selectCommand,
44+
IBinder binder,
45+
string[] suppliedPropertyNames, CancellationToken cancellationToken);
46+
}
47+
}

src/NHibernate/Async/Id/SelectGenerator.cs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
using System.Linq;
1515
using NHibernate.Engine;
1616
using NHibernate.Id.Insert;
17-
using NHibernate.Persister.Collection;
1817
using NHibernate.Persister.Entity;
1918
using NHibernate.SqlCommand;
2019
using NHibernate.SqlTypes;
@@ -37,46 +36,47 @@ public partial class SelectGeneratorDelegate : AbstractSelectingDelegate
3736
protected internal override async Task BindParametersAsync(ISessionImplementor session, DbCommand ps, object entity, CancellationToken cancellationToken)
3837
{
3938
cancellationToken.ThrowIfCancellationRequested();
39+
var entityPersister = (IEntityPersister) persister;
40+
var uniqueKeyPropertyNames = uniqueKeySuppliedPropertyNames ??
41+
PostInsertIdentityPersisterExtension.DetermineNameOfPropertiesToUse(entityPersister);
4042
for (var i = 0; i < uniqueKeyPropertyNames.Length; i++)
4143
{
4244
var uniqueKeyValue = entityPersister.GetPropertyValue(entity, uniqueKeyPropertyNames[i]);
4345
await (uniqueKeyTypes[i].NullSafeSetAsync(ps, uniqueKeyValue, i, session, cancellationToken)).ConfigureAwait(false);
4446
}
4547
}
4648

47-
protected internal override Task BindParametersAsync(ISessionImplementor session, DbCommand ps, IBinder binder, CancellationToken cancellationToken)
49+
protected internal override async Task BindParametersAsync(ISessionImplementor session, DbCommand ps, IBinder binder, CancellationToken cancellationToken)
4850
{
49-
if (cancellationToken.IsCancellationRequested)
50-
{
51-
return Task.FromCanceled<object>(cancellationToken);
52-
}
53-
try
51+
cancellationToken.ThrowIfCancellationRequested();
52+
// 6.0 TODO: remove the "if" block and do the other TODO of this method
53+
if (persister is IEntityPersister)
5454
{
55-
if (entityPersister != null)
56-
{
57-
// 6.0 TODO: inline the call.
5855
#pragma warning disable 618
59-
return BindParametersAsync(session, ps, binder.Entity, cancellationToken);
56+
await (BindParametersAsync(session, ps, binder.Entity, cancellationToken)).ConfigureAwait(false);
6057
#pragma warning restore 618
61-
}
62-
else
63-
{
64-
return binder.BindValuesAsync(ps, cancellationToken);
65-
}
58+
return;
6659
}
67-
catch (Exception ex)
60+
61+
if (persister is ICompositeKeyPostInsertIdentityPersister multiProperties)
6862
{
69-
return Task.FromException<object>(ex);
63+
await (multiProperties.BindSelectByUniqueKeyAsync(session, ps, binder, uniqueKeySuppliedPropertyNames, cancellationToken)).ConfigureAwait(false);
64+
return;
7065
}
66+
67+
// 6.0 TODO: remove by merging ICompositeKeyPostInsertIdentityPersister in IPostInsertIdentityPersister
68+
await (binder.BindValuesAsync(ps, cancellationToken)).ConfigureAwait(false);
7169
}
7270

7371
protected internal override async Task<object> GetResultAsync(ISessionImplementor session, DbDataReader rs, object entity, CancellationToken cancellationToken)
7472
{
7573
cancellationToken.ThrowIfCancellationRequested();
7674
if (!await (rs.ReadAsync(cancellationToken)).ConfigureAwait(false))
7775
{
78-
throw new IdentifierGenerationException(
79-
$"The inserted row could not be located by the unique key: {string.Join(", ", uniqueKeyPropertyNames)}");
76+
var message = "The inserted row could not be located by the unique key";
77+
if (uniqueKeySuppliedPropertyNames != null)
78+
message = $"{message} (supplied unique key: {string.Join(", ", uniqueKeySuppliedPropertyNames)})";
79+
throw new IdentifierGenerationException(message);
8080
}
8181
return await (idType.NullSafeGetAsync(rs, persister.RootTableKeyColumnNames, session, entity, cancellationToken)).ConfigureAwait(false);
8282
}

src/NHibernate/Async/Persister/Collection/AbstractCollectionPersister.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,21 @@ protected async Task<object> PerformInsertAsync(object ownerId, IPersistentColle
540540

541541
#region NH specific
542542

543+
#region IPostInsertIdentityPersister Members
544+
545+
public Task BindSelectByUniqueKeyAsync(
546+
ISessionImplementor session,
547+
DbCommand selectCommand,
548+
IBinder binder,
549+
string[] suppliedPropertyNames, CancellationToken cancellationToken)
550+
{
551+
if (cancellationToken.IsCancellationRequested)
552+
{
553+
return Task.FromCanceled<object>(cancellationToken);
554+
}
555+
return binder.BindValuesAsync(selectCommand, cancellationToken);
556+
}
557+
#endregion
543558

544559
/// <summary>
545560
/// Perform an SQL INSERT, and then retrieve a generated identifier.

src/NHibernate/Async/Persister/Entity/AbstractEntityPersister.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,23 @@ protected Task<object> InsertAsync(object[] fields, bool[] notNull, SqlCommandIn
455455
}
456456
}
457457

458+
public async Task BindSelectByUniqueKeyAsync(
459+
ISessionImplementor session,
460+
DbCommand selectCommand,
461+
IBinder binder,
462+
string[] suppliedPropertyNames, CancellationToken cancellationToken)
463+
{
464+
cancellationToken.ThrowIfCancellationRequested();
465+
var propertyNames = GetUniqueKeyPropertyNames(suppliedPropertyNames);
466+
var parameterTypes = propertyNames.Select(GetPropertyType).ToArray();
467+
var entity = binder.Entity;
468+
for (var i = 0; i < propertyNames.Length; i++)
469+
{
470+
var uniqueKeyValue = GetPropertyValue(entity, propertyNames[i]);
471+
await (parameterTypes[i].NullSafeSetAsync(selectCommand, uniqueKeyValue, i, session, cancellationToken)).ConfigureAwait(false);
472+
}
473+
}
474+
458475
/// <summary>
459476
/// Perform an SQL INSERT.
460477
/// </summary>

src/NHibernate/Id/IPostInsertIdentityPersister.cs

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
using System;
2+
using System.Data.Common;
3+
using System.Linq;
4+
using NHibernate.Engine;
5+
using NHibernate.Id.Insert;
6+
using NHibernate.Persister.Collection;
7+
using NHibernate.Persister.Entity;
28
using NHibernate.SqlCommand;
39
using NHibernate.Type;
410

@@ -9,22 +15,65 @@ internal static class PostInsertIdentityPersisterExtension
915
{
1016
public static SqlString GetSelectByUniqueKeyString(
1117
this IPostInsertIdentityPersister persister,
12-
string[] propertyNames)
18+
string[] suppliedPropertyNames,
19+
out IType[] parameterTypes)
1320
{
1421
if (persister is ICompositeKeyPostInsertIdentityPersister multiProperties)
15-
return multiProperties.GetSelectByUniqueKeyString(propertyNames);
22+
return multiProperties.GetSelectByUniqueKeyString(suppliedPropertyNames, out parameterTypes);
1623

17-
if (propertyNames.Length > 1)
24+
if (suppliedPropertyNames.Length > 1)
1825
{
1926
throw new IdentifierGenerationException(
2027
$"persister {persister} does not implement {nameof(ICompositeKeyPostInsertIdentityPersister)}, " +
2128
$"which is required for selecting by an unique key spanning multiple properties.");
2229
}
2330

31+
if (persister is IEntityPersister entityPersister)
32+
{
33+
var uniqueKeyPropertyNames = suppliedPropertyNames ?? DetermineNameOfPropertiesToUse(entityPersister);
34+
35+
parameterTypes = uniqueKeyPropertyNames.Select(p => entityPersister.GetPropertyType(p)).ToArray();
36+
}
37+
else if (persister is IQueryableCollection collectionPersister)
38+
{
39+
parameterTypes = new[] { collectionPersister.KeyType, collectionPersister.ElementType };
40+
}
41+
else
42+
{
43+
throw new IdentifierGenerationException(
44+
$"Persister type {persister.GetType()} is not supported by post insert identity persisters");
45+
}
2446
#pragma warning disable 618
25-
return persister.GetSelectByUniqueKeyString(propertyNames[0]);
47+
return persister.GetSelectByUniqueKeyString(suppliedPropertyNames[0]);
2648
#pragma warning restore 618
2749
}
50+
51+
internal static string[] DetermineNameOfPropertiesToUse(IEntityPersister persister)
52+
{
53+
var naturalIdPropertyIndices = persister.NaturalIdentifierProperties;
54+
if (naturalIdPropertyIndices == null)
55+
{
56+
throw new IdentifierGenerationException(
57+
"no natural-id property defined; need to specify [key] in generator parameters");
58+
}
59+
60+
foreach (var naturalIdPropertyIndex in naturalIdPropertyIndices)
61+
{
62+
var inclusion = persister.PropertyInsertGenerationInclusions[naturalIdPropertyIndex];
63+
if (inclusion != ValueInclusion.None)
64+
{
65+
throw new IdentifierGenerationException(
66+
"natural-id also defined as insert-generated; need to specify [key] in generator parameters");
67+
}
68+
}
69+
70+
var result = new string[naturalIdPropertyIndices.Length];
71+
for (var i = 0; i < naturalIdPropertyIndices.Length; i++)
72+
{
73+
result[i] = persister.PropertyNames[naturalIdPropertyIndices[i]];
74+
}
75+
return result;
76+
}
2877
}
2978

3079
/// <summary>
@@ -56,7 +105,7 @@ public interface IPostInsertIdentityPersister
56105
/// </param>
57106
/// <returns> The SQL select string </returns>
58107
// Since 5.2
59-
[Obsolete("Have the persister implement IMultiPropertiesPostInsertIdentityPersister and use its GetSelectByUniqueKeyString(string[] propertyNames).")]
108+
[Obsolete("Have the persister implement IMultiPropertiesPostInsertIdentityPersister and use its GetSelectByUniqueKeyString(string[] suppliedPropertyNames, out IType[] parameterTypes).")]
60109
SqlString GetSelectByUniqueKeyString(string propertyName);
61110

62111
#region NH specific
@@ -74,17 +123,36 @@ public interface IPostInsertIdentityPersister
74123
/// An <see cref="IPostInsertIdentityPersister" /> that supports selecting by an unique key spanning
75124
/// multiple properties.
76125
/// </summary>
77-
public interface ICompositeKeyPostInsertIdentityPersister
126+
public partial interface ICompositeKeyPostInsertIdentityPersister
78127
{
79128
/// <summary>
80-
/// Get a SQL select string that performs a select based on a unique
81-
/// key determined by the given array of property names.
129+
/// Get a SQL select string that performs a select based on an unique key, optionnaly determined by
130+
/// the given array of property names.
82131
/// </summary>
83-
/// <param name="propertyNames">
84-
/// The names of the properties which map to the
85-
/// column(s) to use in the select statement restriction.
86-
/// </param>
132+
/// <param name="suppliedPropertyNames">The names of the properties which map to the column(s) to use
133+
/// in the select statement restriction. If supplied, they override the persister logic for determining
134+
/// them.</param>
135+
/// <param name="parameterTypes">In return, the parameter types used by the select string.</param>
87136
/// <returns>The SQL select string.</returns>
88-
SqlString GetSelectByUniqueKeyString(string[] propertyNames);
137+
/// <exception cref="NotSupportedException">thrown if <paramref name="suppliedPropertyNames"/> are
138+
/// specified on a persister which does not allow a custom key.</exception>
139+
SqlString GetSelectByUniqueKeyString(string[] suppliedPropertyNames, out IType[] parameterTypes);
140+
141+
/// <summary>
142+
/// Bind the parameter values of a SQL select command that performs a select based on an unique key.
143+
/// </summary>
144+
/// <param name="session">The current <see cref="ISession" />.</param>
145+
/// <param name="selectCommand">The command.</param>
146+
/// <param name="binder">The id insertion binder.</param>
147+
/// <param name="suppliedPropertyNames">The names of the properties which map to the column(s) to use
148+
/// in the select statement restriction. If supplied, they override the persister logic for determining
149+
/// them.</param>
150+
/// <exception cref="NotSupportedException">thrown if <paramref name="suppliedPropertyNames"/> are
151+
/// specified on a persister which does not allow a custom key.</exception>
152+
void BindSelectByUniqueKey(
153+
ISessionImplementor session,
154+
DbCommand selectCommand,
155+
IBinder binder,
156+
string[] suppliedPropertyNames);
89157
}
90158
}

0 commit comments

Comments
 (0)