Skip to content
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

Throw for LINQ queries on unmapped entities #2504

Merged
merged 8 commits into from
Sep 8, 2020
Merged
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
@@ -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()
{
@@ -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()
40 changes: 39 additions & 1 deletion src/NHibernate.Test/Async/Linq/LinqQuerySamples.cs
Original file line number Diff line number Diff line change
@@ -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()
{
4 changes: 4 additions & 0 deletions src/NHibernate.Test/Linq/FunctionTests.cs
Original file line number Diff line number Diff line change
@@ -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>()
@@ -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>()
@@ -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>()
@@ -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>()
38 changes: 38 additions & 0 deletions src/NHibernate.Test/Linq/LinqQuerySamples.cs
Original file line number Diff line number Diff line change
@@ -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;

@@ -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()
{
10 changes: 9 additions & 1 deletion src/NHibernate.Test/TestCase.cs
Original file line number Diff line number Diff line change
@@ -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;
@@ -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())
{
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)
{
@@ -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)
@@ -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
@@ -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)
{