Skip to content

Commit 61efaf3

Browse files
committed
Update TryGetEntityName method
1 parent 7972d2d commit 61efaf3

File tree

5 files changed

+121
-39
lines changed

5 files changed

+121
-39
lines changed

src/NHibernate.Test/Async/Linq/SelectionTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,18 @@ public async Task CanSelectConditionalSubQueryAsync()
407407
})
408408
.ToListAsync());
409409
Assert.That(list3, Has.Count.GreaterThan(0));
410+
411+
var list4 = await (db.Orders
412+
.Select(o => new
413+
{
414+
Employee = db.Employees.Any(e => e.Superior != null)
415+
? db.Employees
416+
.Where(e => e.Superior != null)
417+
.Select(e => e.Superior).FirstOrDefault()
418+
: o.Employee.Superior != null ? o.Employee.Superior : o.Employee
419+
})
420+
.ToListAsync());
421+
Assert.That(list4, Has.Count.GreaterThan(0));
410422
}
411423

412424
[Test, KnownBug("NH-3045")]

src/NHibernate.Test/Linq/SelectionTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,18 @@ public void CanSelectConditionalSubQuery()
436436
})
437437
.ToList();
438438
Assert.That(list3, Has.Count.GreaterThan(0));
439+
440+
var list4 = db.Orders
441+
.Select(o => new
442+
{
443+
Employee = db.Employees.Any(e => e.Superior != null)
444+
? db.Employees
445+
.Where(e => e.Superior != null)
446+
.Select(e => e.Superior).FirstOrDefault()
447+
: o.Employee.Superior != null ? o.Employee.Superior : o.Employee
448+
})
449+
.ToList();
450+
Assert.That(list4, Has.Count.GreaterThan(0));
439451
}
440452

441453
[Test, KnownBug("NH-3045")]

src/NHibernate/Linq/Visitors/SelectClauseNominator.cs

Lines changed: 4 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using NHibernate.Persister.Entity;
99
using NHibernate.Type;
1010
using Remotion.Linq.Clauses.Expressions;
11+
using NHibernate.Util;
1112

1213
namespace NHibernate.Linq.Visitors
1314
{
@@ -172,7 +173,7 @@ private bool CanBeEvaluatedInHql(MemberExpression memberExpression)
172173
}
173174

174175
// Check whether the member is mapped
175-
var entityName = TryGetEntityName(memberExpression, out var memberPath);
176+
var entityName = ExpressionsHelper.TryGetEntityName(_parameters.SessionFactory, memberExpression, out var memberPath);
176177
if (entityName == null)
177178
{
178179
return false; // Not mapped
@@ -207,9 +208,9 @@ private bool CanBeEvaluatedInHql(BinaryExpression binaryExpression)
207208
var canBeEvaluated = CanBeEvaluatedInHql(binaryExpression.Left) &
208209
CanBeEvaluatedInHql(binaryExpression.Right);
209210

210-
// Subtract datetimes on the client side as its result varies when executed on the server side.
211+
// Subtract datetimes on the client side as the result varies when executed on the server side.
211212
// In Sql Server when using datetime2 subtract is not possbile.
212-
// In Oracle a number is returned that represents the difference bewteen the two in days.
213+
// In Oracle a number is returned that represents the difference between the two in days.
213214
if (new[]
214215
{
215216
ExpressionType.Subtract,
@@ -317,41 +318,6 @@ private void VisitMemberBindings(IEnumerable<MemberBinding> bindings)
317318
}
318319
}
319320

320-
private string TryGetEntityName(MemberExpression memberExpression, out string memberPath)
321-
{
322-
System.Type entityType;
323-
memberPath = memberExpression.Member.Name;
324-
// When having components we need to go though them in order to find the entity
325-
while (memberExpression.Expression is MemberExpression subMemberExpression)
326-
{
327-
// In some cases we can encounter a property representing the entity e.g. [_0].Customer.CustomerId
328-
if (subMemberExpression.NodeType == ExpressionType.MemberAccess)
329-
{
330-
var entityName = _parameters.SessionFactory.TryGetGuessEntityName(memberExpression.Member.ReflectedType);
331-
if (entityName != null)
332-
{
333-
return entityName;
334-
}
335-
}
336-
337-
memberPath = $"{subMemberExpression.Member.Name}.{memberPath}"; // Build a path that can be used to get the property form the entity metadata
338-
memberExpression = subMemberExpression;
339-
}
340-
341-
// Try to get the actual entity type from the query source if possbile as member can be declared
342-
// in a base type
343-
if (memberExpression.Expression is QuerySourceReferenceExpression querySourceReferenceExpression)
344-
{
345-
entityType = querySourceReferenceExpression.Type;
346-
}
347-
else
348-
{
349-
entityType = memberExpression.Member.ReflectedType;
350-
}
351-
352-
return _parameters.SessionFactory.TryGetGuessEntityName(entityType);
353-
}
354-
355321
private static bool ContainsAnyOfTypes(IEnumerable<Expression> expressions, params System.Type[] types)
356322
{
357323
return expressions.Any(o => types.Contains(o.Type));

src/NHibernate/Tuple/Entity/EntityMetamodel.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public class EntityMetamodel
5050
private readonly CascadeStyle[] cascadeStyles;
5151

5252
private readonly Dictionary<string, int?> propertyIndexes = new Dictionary<string, int?>();
53+
private readonly IDictionary<string, IType> _identifierPropertyTypes = new Dictionary<string, IType>();
5354
private readonly bool hasCollections;
5455
private readonly bool hasMutableProperties;
5556
private readonly bool hasLazyProperties;
@@ -428,6 +429,29 @@ private void MapPropertyToIndex(string path, Mapping.Property prop, int i)
428429
}
429430
}
430431

432+
private void MapIdentifierPropertyTypes(IdentifierProperty identifier)
433+
{
434+
MapIdentifierPropertyTypes(identifier.Name, identifier.Type);
435+
}
436+
437+
private void MapIdentifierPropertyTypes(string path, IType propertyType)
438+
{
439+
if (!string.IsNullOrEmpty(path))
440+
{
441+
_identifierPropertyTypes[path] = propertyType;
442+
}
443+
444+
if (propertyType is IAbstractComponentType componentType)
445+
{
446+
for (var i = 0; i < componentType.PropertyNames.Length; i++)
447+
{
448+
MapIdentifierPropertyTypes(
449+
!string.IsNullOrEmpty(path) ? $"{path}.{componentType.PropertyNames[i]}" : componentType.PropertyNames[i],
450+
componentType.Subtypes[i]);
451+
}
452+
}
453+
}
454+
431455
public ISet<string> SubclassEntityNames
432456
{
433457
get { return subclassEntityNames; }
@@ -542,6 +566,11 @@ public int GetPropertyIndex(string propertyName)
542566
return null;
543567
}
544568

569+
internal IType GetIdentifierPropertyType(string memberPath)
570+
{
571+
return _identifierPropertyTypes.TryGetValue(memberPath, out var propertyType) ? propertyType : null;
572+
}
573+
545574
public bool HasCollections
546575
{
547576
get { return hasCollections; }

src/NHibernate/Util/ExpressionsHelper.cs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using System.Linq.Expressions;
22
using System.Reflection;
33
using System;
4+
using NHibernate.Engine;
5+
using NHibernate.Type;
6+
using Remotion.Linq.Clauses;
7+
using Remotion.Linq.Clauses.Expressions;
48

59
namespace NHibernate.Util
610
{
@@ -15,5 +19,64 @@ public static MemberInfo DecodeMemberAccessExpression<TEntity, TResult>(Expressi
1519
}
1620
return ((MemberExpression)expression.Body).Member;
1721
}
22+
23+
internal static string TryGetEntityName(ISessionFactoryImplementor sessionFactory, MemberExpression memberExpression, out string memberPath)
24+
{
25+
string entityName;
26+
memberPath = memberExpression.Member.Name;
27+
// When having components we need to go though them in order to find the entity
28+
while (memberExpression.Expression is MemberExpression subMemberExpression)
29+
{
30+
// In some cases we can encounter a property representing the entity e.g. [_0].Customer.CustomerId
31+
if (subMemberExpression.NodeType == ExpressionType.MemberAccess)
32+
{
33+
entityName = sessionFactory.TryGetGuessEntityName(memberExpression.Member.ReflectedType);
34+
if (entityName != null)
35+
{
36+
return entityName;
37+
}
38+
}
39+
40+
memberPath = $"{subMemberExpression.Member.Name}.{memberPath}"; // Build a path that can be used to get the property form the entity metadata
41+
memberExpression = subMemberExpression;
42+
}
43+
44+
// Try to get the actual entity type from the query source if possbile as member can be declared
45+
// in a base type
46+
if (memberExpression.Expression is QuerySourceReferenceExpression querySourceReferenceExpression)
47+
{
48+
entityName = sessionFactory.TryGetGuessEntityName(querySourceReferenceExpression.Type);
49+
if (entityName != null ||
50+
!(querySourceReferenceExpression.ReferencedQuerySource is IFromClause fromClause) ||
51+
!(fromClause.FromExpression is MemberExpression subMemberExpression))
52+
{
53+
return entityName;
54+
}
55+
56+
// When the member type is not the one that is mapped (e.g. interface) we have to find the first
57+
// mapped entity and calculate the entity name from there
58+
entityName = TryGetEntityName(sessionFactory, subMemberExpression, out var subMemberPath);
59+
if (entityName == null)
60+
{
61+
return null;
62+
}
63+
64+
var persister = sessionFactory.GetEntityPersister(entityName);
65+
var index = persister.EntityMetamodel.GetPropertyIndexOrNull(subMemberPath);
66+
IAssociationType associationType;
67+
if (index.HasValue)
68+
{
69+
associationType = persister.PropertyTypes[index.Value] as IAssociationType;
70+
}
71+
else
72+
{
73+
associationType = persister.EntityMetamodel.GetIdentifierPropertyType(subMemberPath) as IAssociationType;
74+
}
75+
76+
return associationType?.GetAssociatedEntityName(sessionFactory);
77+
}
78+
79+
return sessionFactory.TryGetGuessEntityName(memberExpression.Member.ReflectedType);
80+
}
1881
}
19-
}
82+
}

0 commit comments

Comments
 (0)