Skip to content

Commit cf9b153

Browse files
authored
Merge pull request #28 from servicetitan/non-public-materialization-method
Handle cases when non-public methods are used within queries, which used to cause NullReferenceExecption during translation
2 parents 881c813 + 702c456 commit cf9b153

File tree

2 files changed

+113
-7
lines changed

2 files changed

+113
-7
lines changed
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (C) 2020 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using NUnit.Framework;
9+
using Xtensive.Orm.Configuration;
10+
using Xtensive.Orm.Tests.Issues.MaterializationMethodTestsModel;
11+
12+
namespace Xtensive.Orm.Tests.Issues
13+
{
14+
namespace MaterializationMethodTestsModel
15+
{
16+
[HierarchyRoot]
17+
public class Person : Entity
18+
{
19+
public Person(Session session) : base(session) { }
20+
21+
[Field, Key]
22+
public int Id { get; private set; }
23+
24+
[Field]
25+
public string Name { get; set; }
26+
}
27+
28+
public class PersonModel
29+
{
30+
public int Id { get; set; }
31+
public string Name { get; set; }
32+
}
33+
34+
public abstract class ServiceBase<TEntity, TModel> where TModel : class, new() where TEntity : class, IEntity
35+
{
36+
public IList<TModel> GetAllViaPrivate(Session session) =>
37+
session.Query.All<TEntity>().Select(i => PrivateSelect(i)).ToList();
38+
39+
public IList<TModel> GetAllViaPublic(Session session) =>
40+
session.Query.All<TEntity>().Select(i => PublicSelect(i)).ToList();
41+
42+
public IList<TModel> GetAllViaProtected(Session session) =>
43+
session.Query.All<TEntity>().Select(i => ProtectedSelect(i)).ToList();
44+
45+
public IList<TModel> GetAllViaInternal(Session session) =>
46+
session.Query.All<TEntity>().Select(i => InternalSelect(i)).ToList();
47+
48+
public IList<TModel> GetAllViaProtectedInternal(Session session) =>
49+
session.Query.All<TEntity>().Select(i => ProtectedInternalSelect(i)).ToList();
50+
51+
private TModel PrivateSelect(TEntity entity) => ProtectedSelect(entity);
52+
public TModel PublicSelect(TEntity entity) => ProtectedSelect(entity);
53+
internal TModel InternalSelect(TEntity entity) => ProtectedSelect(entity);
54+
protected internal TModel ProtectedInternalSelect(TEntity entity) => ProtectedSelect(entity);
55+
56+
protected abstract TModel ProtectedSelect(TEntity entity);
57+
}
58+
59+
public class PersonService : ServiceBase<Person, PersonModel>
60+
{
61+
protected override PersonModel ProtectedSelect(Person entity) =>
62+
new PersonModel { Id = entity.Id, Name = entity.Name };
63+
}
64+
}
65+
66+
public class MaterializationMethodTest : AutoBuildTest
67+
{
68+
protected override DomainConfiguration BuildConfiguration()
69+
{
70+
var configuration = base.BuildConfiguration();
71+
var personType = typeof(Person);
72+
configuration.Types.Register(personType.Assembly, personType.Namespace);
73+
return configuration;
74+
}
75+
76+
private static IEnumerable<(string, Func<PersonService, Session, IList<PersonModel>>)> DataExtractorMethods()
77+
{
78+
yield return (nameof(PersonService.GetAllViaPrivate), (service, session) => service.GetAllViaPrivate(session));
79+
yield return (nameof(PersonService.GetAllViaPublic), (service, session) => service.GetAllViaPublic(session));
80+
yield return (nameof(PersonService.GetAllViaInternal), (service, session) => service.GetAllViaInternal(session));
81+
yield return
82+
(nameof(PersonService.GetAllViaProtected), (service, session) => service.GetAllViaProtected(session));
83+
yield return
84+
(nameof(PersonService.GetAllViaProtectedInternal),
85+
(service, session) => service.GetAllViaProtectedInternal(session));
86+
}
87+
88+
[Test]
89+
[TestCaseSource(nameof(DataExtractorMethods))]
90+
public void MaterializationByMethodOfBaseService(
91+
(string name, Func<PersonService, Session, IList<PersonModel>> getPeopleMethod) testCase)
92+
{
93+
using var session = Domain.OpenSession();
94+
using var _ = session.OpenTransaction();
95+
var personService = new PersonService();
96+
var people = testCase.getPeopleMethod(personService, session);
97+
Assert.AreEqual(0, people.Count);
98+
}
99+
}
100+
}

Orm/Xtensive.Orm/Orm/Linq/MemberCompilation/MemberCompilerProvider.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -338,21 +338,27 @@ private static MemberInfo GetCanonicalMember(MemberInfo member)
338338
targetType = targetType.GetGenericTypeDefinition();
339339
if (canonicalMember is FieldInfo)
340340
canonicalMember = targetType.GetField(canonicalMember.Name);
341-
else if (canonicalMember is MethodInfo)
342-
canonicalMember = GetCanonicalMethod((MethodInfo) canonicalMember, targetType.GetMethods());
341+
else if (canonicalMember is MethodInfo methodInfo) {
342+
canonicalMember = GetCanonicalMethod(methodInfo, targetType.GetMethods());
343+
}
343344
else if (canonicalMember is ConstructorInfo)
344345
canonicalMember = GetCanonicalMethod((ConstructorInfo) canonicalMember, targetType.GetConstructors());
345346
else
346347
canonicalMember = null;
347348
}
348349

349-
var declaratedType = canonicalMember.DeclaringType;
350-
if (targetType.IsEnum)
351-
if (targetType!=declaratedType)
352-
canonicalMember = GetCanonicalMethod((MethodInfo) canonicalMember, declaratedType.GetMethods());
350+
if (canonicalMember == null) {
351+
return null;
352+
}
353+
354+
if (targetType.IsEnum) {
355+
var declaringType = canonicalMember.DeclaringType;
356+
if (targetType != declaringType)
357+
canonicalMember = GetCanonicalMethod((MethodInfo) canonicalMember, declaringType.GetMethods());
353358
else
354359
canonicalMember = GetCanonicalMethod((MethodInfo) canonicalMember, targetType.GetMethods());
355-
360+
}
361+
356362
return canonicalMember;
357363
}
358364

0 commit comments

Comments
 (0)