Skip to content

Commit 73d9854

Browse files
authored
Throw for LINQ queries on unmapped entities (#2504)
Fixes #1106
1 parent 9a5a50e commit 73d9854

File tree

7 files changed

+109
-62
lines changed

7 files changed

+109
-62
lines changed

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

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -367,16 +367,6 @@ where item.Id.Equals(-1)
367367
await (ObjectDumper.WriteAsync(query));
368368
}
369369

370-
[Test]
371-
public async Task WhereShortEqualAsync()
372-
{
373-
var query = from item in session.Query<Foo>()
374-
where item.Short.Equals(-1)
375-
select item;
376-
377-
await (ObjectDumper.WriteAsync(query));
378-
}
379-
380370
[Test]
381371
public async Task WhereBoolConstantEqualAsync()
382372
{
@@ -458,36 +448,6 @@ where item.BodyWeight.Equals(-1)
458448

459449
await (ObjectDumper.WriteAsync(query));
460450
}
461-
462-
[Test]
463-
public async Task WhereFloatEqualAsync()
464-
{
465-
var query = from item in session.Query<Foo>()
466-
where item.Float.Equals(-1)
467-
select item;
468-
469-
await (ObjectDumper.WriteAsync(query));
470-
}
471-
472-
[Test]
473-
public async Task WhereCharEqualAsync()
474-
{
475-
var query = from item in session.Query<Foo>()
476-
where item.Char.Equals('A')
477-
select item;
478-
479-
await (ObjectDumper.WriteAsync(query));
480-
}
481-
482-
[Test]
483-
public async Task WhereByteEqualAsync()
484-
{
485-
var query = from item in session.Query<Foo>()
486-
where item.Byte.Equals(1)
487-
select item;
488-
489-
await (ObjectDumper.WriteAsync(query));
490-
}
491451

492452
[Test]
493453
public async Task WhereDecimalEqualAsync()

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,54 @@
1313
using System.Collections.Generic;
1414
using System.Linq;
1515
using NHibernate.DomainModel.Northwind.Entities;
16+
using NHibernate.Hql.Ast.ANTLR;
17+
using NHibernate.Linq;
1618
using NSubstitute;
1719
using NUnit.Framework;
18-
using NHibernate.Linq;
1920

2021
namespace NHibernate.Test.Linq
2122
{
2223
using System.Threading.Tasks;
2324
[TestFixture]
2425
public class LinqQuerySamplesAsync : LinqTestCase
2526
{
27+
class NotMappedEntity
28+
{
29+
public virtual int Id { get; set; }
30+
public virtual string Name { get; set; }
31+
}
32+
33+
[Test]
34+
public void ShouldThrowForQueryOnNotMappedEntityAsync()
35+
{
36+
var querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.Query<NotMappedEntity>().Select(x => x.Id).ToListAsync());
37+
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));
38+
}
39+
40+
[Test]
41+
public void ShouldThrowForQueryOnNotMappedEntityNameAsync()
42+
{
43+
var entityName = "SomeNamespace.NotMappedEntityName";
44+
var querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.Query<NotMappedEntity>(entityName).ToListAsync());
45+
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
46+
}
47+
48+
[Test]
49+
public void ShouldThrowForDmlQueryOnNotMappedEntityAsync()
50+
{
51+
Assert.Multiple(
52+
() =>
53+
{
54+
var querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.Query<NotMappedEntity>().DeleteAsync());
55+
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));
56+
57+
var entityName = "SomeNamespace.NotMappedEntityName";
58+
querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.DeleteAsync($"from {entityName}"));
59+
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
60+
return Task.CompletedTask;
61+
});
62+
}
63+
2664
[Test]
2765
public async Task GroupTwoQueriesAndSumAsync()
2866
{

src/NHibernate.Test/Linq/FunctionTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ where item.Id.Equals(-1)
356356
}
357357

358358
[Test]
359+
[Ignore("Not mapped entity")]
359360
public void WhereShortEqual()
360361
{
361362
var query = from item in session.Query<Foo>()
@@ -448,6 +449,7 @@ where item.BodyWeight.Equals(-1)
448449
}
449450

450451
[Test]
452+
[Ignore("Not mapped entity")]
451453
public void WhereFloatEqual()
452454
{
453455
var query = from item in session.Query<Foo>()
@@ -458,6 +460,7 @@ where item.Float.Equals(-1)
458460
}
459461

460462
[Test]
463+
[Ignore("Not mapped entity")]
461464
public void WhereCharEqual()
462465
{
463466
var query = from item in session.Query<Foo>()
@@ -468,6 +471,7 @@ where item.Char.Equals('A')
468471
}
469472

470473
[Test]
474+
[Ignore("Not mapped entity")]
471475
public void WhereByteEqual()
472476
{
473477
var query = from item in session.Query<Foo>()

src/NHibernate.Test/Linq/LinqQuerySamples.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using System.Collections.Generic;
44
using System.Linq;
55
using NHibernate.DomainModel.Northwind.Entities;
6+
using NHibernate.Hql.Ast.ANTLR;
7+
using NHibernate.Linq;
68
using NSubstitute;
79
using NUnit.Framework;
810

@@ -11,6 +13,42 @@ namespace NHibernate.Test.Linq
1113
[TestFixture]
1214
public class LinqQuerySamples : LinqTestCase
1315
{
16+
class NotMappedEntity
17+
{
18+
public virtual int Id { get; set; }
19+
public virtual string Name { get; set; }
20+
}
21+
22+
[Test]
23+
public void ShouldThrowForQueryOnNotMappedEntity()
24+
{
25+
var querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Query<NotMappedEntity>().Select(x => x.Id).ToList());
26+
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));
27+
}
28+
29+
[Test]
30+
public void ShouldThrowForQueryOnNotMappedEntityName()
31+
{
32+
var entityName = "SomeNamespace.NotMappedEntityName";
33+
var querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Query<NotMappedEntity>(entityName).ToList());
34+
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
35+
}
36+
37+
[Test]
38+
public void ShouldThrowForDmlQueryOnNotMappedEntity()
39+
{
40+
Assert.Multiple(
41+
() =>
42+
{
43+
var querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Query<NotMappedEntity>().Delete());
44+
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));
45+
46+
var entityName = "SomeNamespace.NotMappedEntityName";
47+
querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Delete($"from {entityName}"));
48+
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
49+
});
50+
}
51+
1452
[Test]
1553
public void GroupTwoQueriesAndSum()
1654
{

src/NHibernate.Test/TestCase.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Data;
55
using System.Data.Common;
6+
using System.Linq;
67
using System.Reflection;
78
using log4net;
89
using NHibernate.Cfg;
@@ -230,13 +231,20 @@ private string GetCombinedFailureMessage(TestContext.ResultAdapter result, strin
230231

231232
protected virtual bool CheckDatabaseWasCleaned()
232233
{
233-
if (Sfi.GetAllClassMetadata().Count == 0)
234+
var allClassMetadata = Sfi.GetAllClassMetadata();
235+
if (allClassMetadata.Count == 0)
234236
{
235237
// Return early in the case of no mappings, also avoiding
236238
// a warning when executing the HQL below.
237239
return true;
238240
}
239241

242+
var explicitPolymorphismEntities = allClassMetadata.Values.Where(x => x is NHibernate.Persister.Entity.IQueryable queryable && queryable.IsExplicitPolymorphism).ToArray();
243+
244+
//TODO: Maybe add explicit load query checks
245+
if (explicitPolymorphismEntities.Length == allClassMetadata.Count)
246+
return true;
247+
240248
bool empty;
241249
using (ISession s = OpenSession())
242250
{

src/NHibernate/Hql/Ast/ANTLR/AstPolymorphicProcessor.cs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
using System.Collections.Generic;
2-
using System.Linq;
32
using NHibernate.Engine;
43
using NHibernate.Hql.Ast.ANTLR.Tree;
4+
using NHibernate.Util;
55

66
namespace NHibernate.Hql.Ast.ANTLR
77
{
88
public class AstPolymorphicProcessor
99
{
1010
private readonly IASTNode _ast;
1111
private readonly ISessionFactoryImplementor _factory;
12-
private IEnumerable<KeyValuePair<IASTNode, IASTNode[]>> _nodeMapping;
12+
private Dictionary<IASTNode, IASTNode[]> _nodeMapping;
1313

1414
private AstPolymorphicProcessor(IASTNode ast, ISessionFactoryImplementor factory)
1515
{
@@ -29,28 +29,27 @@ private IASTNode[] Process()
2929
// Find all the polymorphic query sources
3030
_nodeMapping = new PolymorphicQuerySourceDetector(_factory).Process(_ast);
3131

32-
if (_nodeMapping.Count() > 0)
33-
{
34-
return DuplicateTree().ToArray();
35-
}
36-
else
32+
if (_nodeMapping.Count == 0)
33+
return new[] {_ast};
34+
35+
var parsers = DuplicateTree();
36+
37+
if (parsers.Length == 0)
3738
{
38-
return new[] { _ast };
39+
var entityNames = _nodeMapping.Keys.ToArray(x => PolymorphicQuerySourceDetector.GetClassName(x));
40+
throw new QuerySyntaxException(
41+
entityNames.Length == 1
42+
? entityNames[0] + " is not mapped"
43+
: string.Join(", ", entityNames) + " are not mapped");
3944
}
45+
46+
return parsers;
4047
}
4148

42-
private IEnumerable<IASTNode> DuplicateTree()
49+
private IASTNode[] DuplicateTree()
4350
{
4451
var replacements = CrossJoinDictionaryArrays.PerformCrossJoin(_nodeMapping);
45-
46-
var dups = new IASTNode[replacements.Count()];
47-
48-
for (var i = 0; i < replacements.Count(); i++)
49-
{
50-
dups[i] = DuplicateTree(_ast, replacements[i]);
51-
}
52-
53-
return dups;
52+
return replacements.ToArray(x => DuplicateTree(_ast, x));
5453
}
5554

5655
private static IASTNode DuplicateTree(IASTNode ast, IDictionary<IASTNode, IASTNode> nodeMapping)
@@ -72,4 +71,4 @@ private static IASTNode DuplicateTree(IASTNode ast, IDictionary<IASTNode, IASTNo
7271
return dup;
7372
}
7473
}
75-
}
74+
}

src/NHibernate/Hql/Ast/ANTLR/PolymorphicQuerySourceDetector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ private void AddImplementorsToMap(IASTNode querySource, string className, string
4545
implementors.ToArray(implementor => MakeIdent(querySource, implementor)));
4646
}
4747

48-
private static string GetClassName(IASTNode querySource)
48+
internal static string GetClassName(IASTNode querySource)
4949
{
5050
switch (querySource.Type)
5151
{

0 commit comments

Comments
 (0)