Skip to content

Throw for LINQ queries on unmapped entities #2504

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 0 additions & 40 deletions src/NHibernate.Test/Async/Linq/FunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -367,16 +367,6 @@ where item.Id.Equals(-1)
await (ObjectDumper.WriteAsync(query));
}

[Test]
public async Task WhereShortEqualAsync()
{
var query = from item in session.Query<Foo>()
where item.Short.Equals(-1)
select item;

await (ObjectDumper.WriteAsync(query));
}

[Test]
public async Task WhereBoolConstantEqualAsync()
{
Expand Down Expand Up @@ -458,36 +448,6 @@ where item.BodyWeight.Equals(-1)

await (ObjectDumper.WriteAsync(query));
}

[Test]
public async Task WhereFloatEqualAsync()
{
var query = from item in session.Query<Foo>()
where item.Float.Equals(-1)
select item;

await (ObjectDumper.WriteAsync(query));
}

[Test]
public async Task WhereCharEqualAsync()
{
var query = from item in session.Query<Foo>()
where item.Char.Equals('A')
select item;

await (ObjectDumper.WriteAsync(query));
}

[Test]
public async Task WhereByteEqualAsync()
{
var query = from item in session.Query<Foo>()
where item.Byte.Equals(1)
select item;

await (ObjectDumper.WriteAsync(query));
}

[Test]
public async Task WhereDecimalEqualAsync()
Expand Down
40 changes: 39 additions & 1 deletion src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,54 @@
using System.Collections.Generic;
using System.Linq;
using NHibernate.DomainModel.Northwind.Entities;
using NHibernate.Hql.Ast.ANTLR;
using NHibernate.Linq;
using NSubstitute;
using NUnit.Framework;
using NHibernate.Linq;

namespace NHibernate.Test.Linq
{
using System.Threading.Tasks;
[TestFixture]
public class LinqQuerySamplesAsync : LinqTestCase
{
class NotMappedEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}

[Test]
public void ShouldThrowForQueryOnNotMappedEntityAsync()
{
var querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.Query<NotMappedEntity>().Select(x => x.Id).ToListAsync());
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));
}

[Test]
public void ShouldThrowForQueryOnNotMappedEntityNameAsync()
{
var entityName = "SomeNamespace.NotMappedEntityName";
var querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.Query<NotMappedEntity>(entityName).ToListAsync());
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
}

[Test]
public void ShouldThrowForDmlQueryOnNotMappedEntityAsync()
{
Assert.Multiple(
() =>
{
var querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.Query<NotMappedEntity>().DeleteAsync());
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));

var entityName = "SomeNamespace.NotMappedEntityName";
querySyntaxException = Assert.ThrowsAsync<QuerySyntaxException>(() => session.DeleteAsync($"from {entityName}"));
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
return Task.CompletedTask;
});
}

[Test]
public async Task GroupTwoQueriesAndSumAsync()
{
Expand Down
4 changes: 4 additions & 0 deletions src/NHibernate.Test/Linq/FunctionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ where item.Id.Equals(-1)
}

[Test]
[Ignore("Not mapped entity")]
public void WhereShortEqual()
{
var query = from item in session.Query<Foo>()
Expand Down Expand Up @@ -448,6 +449,7 @@ where item.BodyWeight.Equals(-1)
}

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

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

[Test]
[Ignore("Not mapped entity")]
public void WhereByteEqual()
{
var query = from item in session.Query<Foo>()
Expand Down
38 changes: 38 additions & 0 deletions src/NHibernate.Test/Linq/LinqQuerySamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Collections.Generic;
using System.Linq;
using NHibernate.DomainModel.Northwind.Entities;
using NHibernate.Hql.Ast.ANTLR;
using NHibernate.Linq;
using NSubstitute;
using NUnit.Framework;

Expand All @@ -11,6 +13,42 @@ namespace NHibernate.Test.Linq
[TestFixture]
public class LinqQuerySamples : LinqTestCase
{
class NotMappedEntity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
}

[Test]
public void ShouldThrowForQueryOnNotMappedEntity()
{
var querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Query<NotMappedEntity>().Select(x => x.Id).ToList());
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));
}

[Test]
public void ShouldThrowForQueryOnNotMappedEntityName()
{
var entityName = "SomeNamespace.NotMappedEntityName";
var querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Query<NotMappedEntity>(entityName).ToList());
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
}

[Test]
public void ShouldThrowForDmlQueryOnNotMappedEntity()
{
Assert.Multiple(
() =>
{
var querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Query<NotMappedEntity>().Delete());
Assert.That(querySyntaxException.Message, Does.Contain(nameof(NotMappedEntity)));

var entityName = "SomeNamespace.NotMappedEntityName";
querySyntaxException = Assert.Throws<QuerySyntaxException>(() => session.Delete($"from {entityName}"));
Assert.That(querySyntaxException.Message, Does.Contain(entityName));
});
}

[Test]
public void GroupTwoQueriesAndSum()
{
Expand Down
10 changes: 9 additions & 1 deletion src/NHibernate.Test/TestCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using log4net;
using NHibernate.Cfg;
Expand Down Expand Up @@ -230,13 +231,20 @@ private string GetCombinedFailureMessage(TestContext.ResultAdapter result, strin

protected virtual bool CheckDatabaseWasCleaned()
{
if (Sfi.GetAllClassMetadata().Count == 0)
var allClassMetadata = Sfi.GetAllClassMetadata();
if (allClassMetadata.Count == 0)
{
// Return early in the case of no mappings, also avoiding
// a warning when executing the HQL below.
return true;
}

var explicitPolymorphismEntities = allClassMetadata.Values.Where(x => x is NHibernate.Persister.Entity.IQueryable queryable && queryable.IsExplicitPolymorphism).ToArray();

//TODO: Maybe add explicit load query checks
if (explicitPolymorphismEntities.Length == allClassMetadata.Count)
return true;

bool empty;
using (ISession s = OpenSession())
{
Expand Down
37 changes: 18 additions & 19 deletions src/NHibernate/Hql/Ast/ANTLR/AstPolymorphicProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using System.Collections.Generic;
using System.Linq;
using NHibernate.Engine;
using NHibernate.Hql.Ast.ANTLR.Tree;
using NHibernate.Util;

namespace NHibernate.Hql.Ast.ANTLR
{
public class AstPolymorphicProcessor
{
private readonly IASTNode _ast;
private readonly ISessionFactoryImplementor _factory;
private IEnumerable<KeyValuePair<IASTNode, IASTNode[]>> _nodeMapping;
private Dictionary<IASTNode, IASTNode[]> _nodeMapping;

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

if (_nodeMapping.Count() > 0)
{
return DuplicateTree().ToArray();
}
else
if (_nodeMapping.Count == 0)
return new[] {_ast};

var parsers = DuplicateTree();

if (parsers.Length == 0)
{
return new[] { _ast };
var entityNames = _nodeMapping.Keys.ToArray(x => PolymorphicQuerySourceDetector.GetClassName(x));
throw new QuerySyntaxException(
entityNames.Length == 1
? entityNames[0] + " is not mapped"
: string.Join(", ", entityNames) + " are not mapped");
}

return parsers;
}

private IEnumerable<IASTNode> DuplicateTree()
private IASTNode[] DuplicateTree()
{
var replacements = CrossJoinDictionaryArrays.PerformCrossJoin(_nodeMapping);

var dups = new IASTNode[replacements.Count()];

for (var i = 0; i < replacements.Count(); i++)
{
dups[i] = DuplicateTree(_ast, replacements[i]);
}

return dups;
return replacements.ToArray(x => DuplicateTree(_ast, x));
}

private static IASTNode DuplicateTree(IASTNode ast, IDictionary<IASTNode, IASTNode> nodeMapping)
Expand All @@ -72,4 +71,4 @@ private static IASTNode DuplicateTree(IASTNode ast, IDictionary<IASTNode, IASTNo
return dup;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private void AddImplementorsToMap(IASTNode querySource, string className, string
implementors.ToArray(implementor => MakeIdent(querySource, implementor)));
}

private static string GetClassName(IASTNode querySource)
internal static string GetClassName(IASTNode querySource)
{
switch (querySource.Type)
{
Expand Down