Skip to content

Commit 68b0f51

Browse files
committed
Update TryGetEntityName method
1 parent e1c7afe commit 68b0f51

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
@@ -673,34 +673,23 @@ private IType GetType(Expression expression)
673673
}
674674

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

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

703-
return _parameters.SessionFactory.TryGetGuessEntityName(entityType);
689+
var index = persister.EntityMetamodel.GetPropertyIndexOrNull(memberPath);
690+
return !index.HasValue
691+
? TypeFactory.GetDefaultTypeFor(expression.Type) // Not mapped
692+
: persister.EntityMetamodel.PropertyTypes[index.Value];
704693
}
705694
}
706695
}

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;
@@ -93,6 +94,7 @@ public EntityMetamodel(PersistentClass persistentClass, ISessionFactoryImplement
9394

9495
identifierProperty = PropertyFactory.BuildIdentifierProperty(persistentClass,
9596
sessionFactory.GetIdentifierGenerator(rootName));
97+
MapIdentifierPropertyTypes(identifierProperty);
9698

9799
versioned = persistentClass.IsVersioned;
98100

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

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

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