diff --git a/src/NHibernate.Test/Async/NHSpecificTest/NH3565/FixtureByCode.cs b/src/NHibernate.Test/Async/NHSpecificTest/NH3565/FixtureByCode.cs
new file mode 100644
index 00000000000..07047559dcc
--- /dev/null
+++ b/src/NHibernate.Test/Async/NHSpecificTest/NH3565/FixtureByCode.cs
@@ -0,0 +1,121 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by AsyncGenerator.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+
+using System.Data;
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NHibernate.SqlTypes;
+using NUnit.Framework;
+using NHibernate.Linq;
+
+namespace NHibernate.Test.NHSpecificTest.NH3565
+{
+ using System.Threading.Tasks;
+ [TestFixture]
+ public class ByCodeFixtureAsync : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name, m =>
+ {
+ m.Type(NHibernateUtil.AnsiString);
+ m.Length(10);
+ });
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return base.AppliesTo(dialect)
+ //Dialects like SQL Server CE, Firebird don't distinguish AnsiString from String
+ && Dialect.GetTypeName(new SqlType(DbType.AnsiString)) != Dialect.GetTypeName(new SqlType(DbType.String));
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity {Name = "Bob"};
+ session.Save(e1);
+
+ var e2 = new Entity {Name = "Sally"};
+ session.Save(e2);
+
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public async Task ParameterTypeForLikeIsProperlyDetectedAsync()
+ {
+ using (var logSpy = new SqlLogSpy())
+ using (var session = OpenSession())
+ {
+ var result = from e in session.Query()
+ where NHibernate.Linq.SqlMethods.Like(e.Name, "Bob")
+ select e;
+
+ Assert.That(await (result.ToListAsync()), Has.Count.EqualTo(1));
+ Assert.That(logSpy.GetWholeLog(), Does.Contain("Type: AnsiString"));
+ }
+ }
+
+ [KnownBug("Not fixed yet")]
+ [Test]
+ public async Task ParameterTypeForContainsIsProperlyDetectedAsync()
+ {
+ using (var logSpy = new SqlLogSpy())
+ using (var session = OpenSession())
+ {
+ var result = from e in session.Query()
+ where e.Name.Contains("Bob")
+ select e;
+
+ Assert.That(await (result.ToListAsync()), Has.Count.EqualTo(1));
+ Assert.That(logSpy.GetWholeLog(), Does.Contain("Type: AnsiString"));
+ }
+ }
+
+ [KnownBug("Not fixed yet")]
+ [Test]
+ public async Task ParameterTypeForStartsWithIsProperlyDetectedAsync()
+ {
+ using (var logSpy = new SqlLogSpy())
+ using (var session = OpenSession())
+ {
+ var result = from e in session.Query()
+ where e.Name.StartsWith("Bob")
+ select e;
+
+ Assert.That(await (result.ToListAsync()), Has.Count.EqualTo(1));
+ Assert.That(logSpy.GetWholeLog(), Does.Contain("Type: AnsiString"));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/NH3565/Entity.cs b/src/NHibernate.Test/NHSpecificTest/NH3565/Entity.cs
new file mode 100644
index 00000000000..4cc0a2c4513
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3565/Entity.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace NHibernate.Test.NHSpecificTest.NH3565
+{
+ class Entity
+ {
+ public virtual Guid Id { get; set; }
+ public virtual string Name { get; set; }
+ }
+}
diff --git a/src/NHibernate.Test/NHSpecificTest/NH3565/FixtureByCode.cs b/src/NHibernate.Test/NHSpecificTest/NH3565/FixtureByCode.cs
new file mode 100644
index 00000000000..f683293d591
--- /dev/null
+++ b/src/NHibernate.Test/NHSpecificTest/NH3565/FixtureByCode.cs
@@ -0,0 +1,109 @@
+using System.Data;
+using System.Linq;
+using NHibernate.Cfg.MappingSchema;
+using NHibernate.Mapping.ByCode;
+using NHibernate.SqlTypes;
+using NUnit.Framework;
+
+namespace NHibernate.Test.NHSpecificTest.NH3565
+{
+ [TestFixture]
+ public class ByCodeFixture : TestCaseMappingByCode
+ {
+ protected override HbmMapping GetMappings()
+ {
+ var mapper = new ModelMapper();
+ mapper.Class(rc =>
+ {
+ rc.Id(x => x.Id, m => m.Generator(Generators.GuidComb));
+ rc.Property(x => x.Name, m =>
+ {
+ m.Type(NHibernateUtil.AnsiString);
+ m.Length(10);
+ });
+ });
+
+ return mapper.CompileMappingForAllExplicitlyAddedEntities();
+ }
+
+ protected override bool AppliesTo(Dialect.Dialect dialect)
+ {
+ return base.AppliesTo(dialect)
+ //Dialects like SQL Server CE, Firebird don't distinguish AnsiString from String
+ && Dialect.GetTypeName(new SqlType(DbType.AnsiString)) != Dialect.GetTypeName(new SqlType(DbType.String));
+ }
+
+ protected override void OnSetUp()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ var e1 = new Entity {Name = "Bob"};
+ session.Save(e1);
+
+ var e2 = new Entity {Name = "Sally"};
+ session.Save(e2);
+
+ transaction.Commit();
+ }
+ }
+
+ protected override void OnTearDown()
+ {
+ using (var session = OpenSession())
+ using (var transaction = session.BeginTransaction())
+ {
+ session.CreateQuery("delete from System.Object").ExecuteUpdate();
+
+ transaction.Commit();
+ }
+ }
+
+ [Test]
+ public void ParameterTypeForLikeIsProperlyDetected()
+ {
+ using (var logSpy = new SqlLogSpy())
+ using (var session = OpenSession())
+ {
+ var result = from e in session.Query()
+ where NHibernate.Linq.SqlMethods.Like(e.Name, "Bob")
+ select e;
+
+ Assert.That(result.ToList(), Has.Count.EqualTo(1));
+ Assert.That(logSpy.GetWholeLog(), Does.Contain("Type: AnsiString"));
+ }
+ }
+
+ [KnownBug("Not fixed yet")]
+ [Test]
+ public void ParameterTypeForContainsIsProperlyDetected()
+ {
+ using (var logSpy = new SqlLogSpy())
+ using (var session = OpenSession())
+ {
+ var result = from e in session.Query()
+ where e.Name.Contains("Bob")
+ select e;
+
+ Assert.That(result.ToList(), Has.Count.EqualTo(1));
+ Assert.That(logSpy.GetWholeLog(), Does.Contain("Type: AnsiString"));
+ }
+ }
+
+ [KnownBug("Not fixed yet")]
+ [Test]
+ public void ParameterTypeForStartsWithIsProperlyDetected()
+ {
+ using (var logSpy = new SqlLogSpy())
+ using (var session = OpenSession())
+ {
+ var result = from e in session.Query()
+ where e.Name.StartsWith("Bob")
+ select e;
+
+ Assert.That(result.ToList(), Has.Count.EqualTo(1));
+ Assert.That(logSpy.GetWholeLog(), Does.Contain("Type: AnsiString"));
+ }
+ }
+ }
+}
diff --git a/src/NHibernate/Hql/Ast/ANTLR/Tree/CaseNode.cs b/src/NHibernate/Hql/Ast/ANTLR/Tree/CaseNode.cs
index c48fd3aa248..93a62499634 100644
--- a/src/NHibernate/Hql/Ast/ANTLR/Tree/CaseNode.cs
+++ b/src/NHibernate/Hql/Ast/ANTLR/Tree/CaseNode.cs
@@ -29,17 +29,38 @@ public override IType DataType
if (ExpectedType != null)
return ExpectedType;
- foreach (var node in GetResultNodes())
+ if (base.DataType != null)
+ return base.DataType;
+
+ var dataType = GetTypeFromResultNodes();
+
+ foreach (var node in GetResultNodes().OfType())
{
- if (node is ISelectExpression select && !(node is ParameterNode))
- return select.DataType;
+ if (node.DataType == null && node is IExpectedTypeAwareNode typeAwareNode)
+ {
+ typeAwareNode.ExpectedType = dataType;
+ }
}
- throw new HibernateException("Unable to determine data type of CASE statement.");
+ base.DataType = dataType;
+ return dataType;
}
set { base.DataType = value; }
}
+ private IType GetTypeFromResultNodes()
+ {
+ foreach (var node in GetResultNodes())
+ {
+ if (node is ISelectExpression select && select.DataType != null)
+ {
+ return select.DataType;
+ }
+ }
+
+ throw new HibernateException("Unable to determine data type of CASE statement.");
+ }
+
public IEnumerable GetResultNodes()
{
for (int i = 0; i < ChildCount; i++)
diff --git a/src/NHibernate/Linq/DefaultQueryProvider.cs b/src/NHibernate/Linq/DefaultQueryProvider.cs
index 912b640a951..681d34105cc 100644
--- a/src/NHibernate/Linq/DefaultQueryProvider.cs
+++ b/src/NHibernate/Linq/DefaultQueryProvider.cs
@@ -265,7 +265,11 @@ private static void SetParameters(IQuery query, IDictionary constantExpressions,
ConstantTypeLocatorVisitor visitor,
NamedParameter namedParameter,
- out bool tryProcessInHql)
+ out bool isGuessedType)
{
- tryProcessInHql = false;
+ isGuessedType = false;
// All constant expressions have the same type/value
var constantExpression = constantExpressions.First();
var constantType = constantExpression.Type.UnwrapIfNullable();
@@ -159,10 +159,7 @@ private static IType GetParameterType(
return candidateType;
}
- if (visitor.NotGuessableConstants.Contains(constantExpression) && constantExpression.Value != null)
- {
- tryProcessInHql = true;
- }
+ isGuessedType = true;
// No related MemberExpressions was found, guess the type by value or its type when null.
// When a numeric parameter is compared to different columns with different types (e.g. Where(o => o.Single >= singleParam || o.Double <= singleParam))
@@ -174,13 +171,10 @@ private static IType GetParameterType(
private class ConstantTypeLocatorVisitor : RelinqExpressionVisitor
{
- private bool _hqlGenerator;
private readonly bool _removeMappedAsCalls;
private readonly System.Type _targetType;
private readonly IDictionary _parameters;
private readonly ISessionFactoryImplementor _sessionFactory;
- private readonly ILinqToHqlGeneratorsRegistry _functionRegistry;
- public readonly HashSet NotGuessableConstants = new HashSet();
public readonly Dictionary ConstantExpressions =
new Dictionary();
public readonly Dictionary> ParameterConstants =
@@ -198,7 +192,6 @@ public ConstantTypeLocatorVisitor(
_targetType = targetType;
_sessionFactory = sessionFactory;
_parameters = parameters;
- _functionRegistry = sessionFactory.Settings.LinqToHqlGeneratorsRegistry;
}
protected override Expression VisitBinary(BinaryExpression node)
@@ -269,16 +262,6 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
return node;
}
- // For hql method generators we do not want to guess the parameter type here, let hql logic figure it out.
- if (_functionRegistry.TryGetGenerator(node.Method, out _))
- {
- var origHqlGenerator = _hqlGenerator;
- _hqlGenerator = true;
- var expression = base.VisitMethodCall(node);
- _hqlGenerator = origHqlGenerator;
- return expression;
- }
-
return base.VisitMethodCall(node);
}
@@ -289,11 +272,6 @@ protected override Expression VisitConstant(ConstantExpression node)
return node;
}
- if (_hqlGenerator)
- {
- NotGuessableConstants.Add(node);
- }
-
RelatedExpressions.Add(node, new HashSet());
ConstantExpressions.Add(node, null);
if (!ParameterConstants.TryGetValue(param, out var set))
diff --git a/src/NHibernate/Param/NamedParameter.cs b/src/NHibernate/Param/NamedParameter.cs
index 93af89daa0d..a499c1b2514 100644
--- a/src/NHibernate/Param/NamedParameter.cs
+++ b/src/NHibernate/Param/NamedParameter.cs
@@ -20,7 +20,7 @@ internal NamedParameter(string name, object value, IType type, bool isCollection
public string Name { get; private set; }
public object Value { get; internal set; }
public IType Type { get; internal set; }
- internal bool IsGuessedType { get; set; }
+ internal bool IsGuessedType { get; set; } = true;
public virtual bool IsCollection { get; }