Skip to content

Commit 8a04fa4

Browse files
committed
Update TryGetEntityName method
1 parent 21e7dd4 commit 8a04fa4

File tree

5 files changed

+121
-25
lines changed

5 files changed

+121
-25
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,10 @@ public async Task CanProjectWithCastAsync()
309309

310310
var names5 = await (db.Users.Select(p => new { p1 = (p as IUser).Name }).ToListAsync());
311311
Assert.AreEqual(3, names5.Count);
312+
313+
var names6 = await (db.Users.Select(p => new { p1 = (long) p.Id }).ToListAsync());
314+
Assert.AreEqual(3, names6.Count);
315+
312316
// ReSharper restore RedundantCast
313317
}
314318

src/NHibernate.Test/Linq/SelectionTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,10 @@ public void CanProjectWithCast()
348348

349349
var names5 = db.Users.Select(p => new { p1 = (p as IUser).Name }).ToList();
350350
Assert.AreEqual(3, names5.Count);
351+
352+
var names6 = db.Users.Select(p => new { p1 = (long) p.Id }).ToList();
353+
Assert.AreEqual(3, names6.Count);
354+
351355
// ReSharper restore RedundantCast
352356
}
353357

src/NHibernate/Linq/Visitors/HqlGeneratorExpressionVisitor.cs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -671,34 +671,23 @@ private IType GetType(Expression expression)
671671
}
672672

673673
// Try to get the mapped type for the member as it may be a non default one
674-
var entityName = TryGetEntityName(memberExpression);
674+
var entityName = ExpressionsHelper.TryGetEntityName(_parameters.SessionFactory, memberExpression, out var memberPath);
675675
if (entityName == null)
676676
{
677677
return TypeFactory.GetDefaultTypeFor(expression.Type); // Not mapped
678678
}
679679

680680
var persister = _parameters.SessionFactory.GetEntityPersister(entityName);
681-
var index = persister.EntityMetamodel.GetPropertyIndexOrNull(memberExpression.Member.Name);
682-
return !index.HasValue
683-
? TypeFactory.GetDefaultTypeFor(expression.Type) // Not mapped
684-
: persister.EntityMetamodel.PropertyTypes[index.Value];
685-
}
686-
687-
private string TryGetEntityName(MemberExpression memberExpression)
688-
{
689-
System.Type entityType;
690-
// Try to get the actual entity type from the query source if possbile as member can be declared
691-
// in a base type
692-
if (memberExpression.Expression is QuerySourceReferenceExpression querySourceReferenceExpression)
681+
var type = persister.EntityMetamodel.GetIdentifierPropertyType(memberPath);
682+
if (type != null)
693683
{
694-
entityType = querySourceReferenceExpression.Type;
695-
}
696-
else
697-
{
698-
entityType = memberExpression.Member.ReflectedType;
684+
return type;
699685
}
700686

701-
return _parameters.SessionFactory.TryGetGuessEntityName(entityType);
687+
var index = persister.EntityMetamodel.GetPropertyIndexOrNull(memberPath);
688+
return !index.HasValue
689+
? TypeFactory.GetDefaultTypeFor(expression.Type) // Not mapped
690+
: persister.EntityMetamodel.PropertyTypes[index.Value];
702691
}
703692
}
704693
}

src/NHibernate/Tuple/Entity/EntityMetamodel.cs

Lines changed: 41 additions & 5 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;
@@ -91,6 +92,7 @@ public EntityMetamodel(PersistentClass persistentClass, ISessionFactoryImplement
9192

9293
identifierProperty = PropertyFactory.BuildIdentifierProperty(persistentClass,
9394
sessionFactory.GetIdentifierGenerator(rootName));
95+
MapIdentifierPropertyTypes(identifierProperty);
9496

9597
versioned = persistentClass.IsVersioned;
9698

@@ -409,13 +411,42 @@ private bool HasPartialUpdateComponentGeneration(Mapping.Component component)
409411

410412
private void MapPropertyToIndex(Mapping.Property prop, int i)
411413
{
412-
propertyIndexes[prop.Name] = i;
413-
Mapping.Component comp = prop.Value as Mapping.Component;
414-
if (comp != null)
414+
MapPropertyToIndex(null, prop, i);
415+
}
416+
417+
private void MapPropertyToIndex(string path, Mapping.Property prop, int i)
418+
{
419+
propertyIndexes[!string.IsNullOrEmpty(path) ? $"{path}.{prop.Name}" : prop.Name] = i;
420+
if (!(prop.Value is Mapping.Component comp))
421+
{
422+
return;
423+
}
424+
425+
foreach (var subprop in comp.PropertyIterator)
426+
{
427+
MapPropertyToIndex(!string.IsNullOrEmpty(path) ? $"{path}.{prop.Name}" : prop.Name, subprop, i);
428+
}
429+
}
430+
431+
private void MapIdentifierPropertyTypes(IdentifierProperty identifier)
432+
{
433+
MapIdentifierPropertyTypes(identifier.Name, identifier.Type);
434+
}
435+
436+
private void MapIdentifierPropertyTypes(string path, IType propertyType)
437+
{
438+
if (!string.IsNullOrEmpty(path))
439+
{
440+
_identifierPropertyTypes[path] = propertyType;
441+
}
442+
443+
if (propertyType is IAbstractComponentType componentType)
415444
{
416-
foreach (Mapping.Property subprop in comp.PropertyIterator)
445+
for (var i = 0; i < componentType.PropertyNames.Length; i++)
417446
{
418-
propertyIndexes[prop.Name + '.' + subprop.Name] = i;
447+
MapIdentifierPropertyTypes(
448+
!string.IsNullOrEmpty(path) ? $"{path}.{componentType.PropertyNames[i]}" : componentType.PropertyNames[i],
449+
componentType.Subtypes[i]);
419450
}
420451
}
421452
}
@@ -534,6 +565,11 @@ public int GetPropertyIndex(string propertyName)
534565
return null;
535566
}
536567

568+
internal IType GetIdentifierPropertyType(string memberPath)
569+
{
570+
return _identifierPropertyTypes.TryGetValue(memberPath, out var propertyType) ? propertyType : null;
571+
}
572+
537573
public bool HasCollections
538574
{
539575
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)