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/Async/IQuery.cs b/src/NHibernate/Async/IQuery.cs
index 31912e9ae7d..74f06cb2eae 100644
--- a/src/NHibernate/Async/IQuery.cs
+++ b/src/NHibernate/Async/IQuery.cs
@@ -13,11 +13,13 @@
using NHibernate.Transform;
using NHibernate.Type;
using System.Collections.Generic;
+using NHibernate.Impl;
namespace NHibernate
{
using System.Threading.Tasks;
using System.Threading;
+
public partial interface IQuery
{
diff --git a/src/NHibernate/IQuery.cs b/src/NHibernate/IQuery.cs
index 4a8d062415a..72e03de07e0 100644
--- a/src/NHibernate/IQuery.cs
+++ b/src/NHibernate/IQuery.cs
@@ -3,9 +3,38 @@
using NHibernate.Transform;
using NHibernate.Type;
using System.Collections.Generic;
+using NHibernate.Impl;
namespace NHibernate
{
+ // 6.0 TODO remove
+ internal static class QueryExtensions
+ {
+ ///
+ /// Bind a value to a named query parameter
+ ///
+ /// The query
+ /// The name of the parameter
+ /// The possibly null parameter value
+ /// The NHibernate .
+ /// If true supplied type is used only if parameter metadata is missing
+ public static void SetParameter(this IQuery query, string name, object val, IType type, bool preferMetadataType)
+ {
+ if (query is AbstractQueryImpl impl)
+ {
+ impl.SetParameter(name, val, type, preferMetadataType);
+ }
+ else
+ {
+ //Let HQL try to process guessed types (hql doesn't support type guessing for NULL)
+ if (type != null && (preferMetadataType == false || val == null))
+ query.SetParameter(name, val, type);
+ else
+ query.SetParameter(name, val);
+ }
+ }
+ }
+
///
/// An object-oriented representation of a NHibernate query.
///
diff --git a/src/NHibernate/Impl/AbstractQueryImpl.cs b/src/NHibernate/Impl/AbstractQueryImpl.cs
index ba46b665466..79b7c721d6b 100644
--- a/src/NHibernate/Impl/AbstractQueryImpl.cs
+++ b/src/NHibernate/Impl/AbstractQueryImpl.cs
@@ -242,17 +242,33 @@ public IQuery SetParameter(int position, object val, IType type)
public IQuery SetParameter(string name, object val, IType type)
{
- if (!parameterMetadata.NamedParameterNames.Contains(name))
- {
- if (shouldIgnoredUnknownNamedParameters)//just ignore it
- return this;
- throw new ArgumentException("Parameter " + name + " does not exist as a named parameter in [" + QueryString + "]");
- }
- else
- {
- namedParameters[name] = new TypedValue(type, val, false);
+ return SetParameter(name, val, type, false);
+ }
+
+ //TODO 6.0: Add to IQuery interface
+ public IQuery SetParameter(string name, object val, IType type, bool preferMetadataType)
+ {
+ if (CheckParameterIgnored(name))
return this;
+
+ if (type == null || preferMetadataType)
+ {
+ type = parameterMetadata.GetNamedParameterExpectedType(name) ?? type ?? ParameterHelper.GuessType(val, session.Factory);
}
+
+ namedParameters[name] = new TypedValue(type, val, false);
+ return this;
+ }
+
+ private bool CheckParameterIgnored(string name)
+ {
+ if (parameterMetadata.NamedParameterNames.Contains(name))
+ return false;
+
+ if (shouldIgnoredUnknownNamedParameters) //just ignore it
+ return true;
+
+ throw new ArgumentException("Parameter " + name + " does not exist as a named parameter in [" + QueryString + "]");
}
public IQuery SetParameter(int position, T val)
@@ -289,29 +305,7 @@ public IQuery SetParameter(string name, T val)
public IQuery SetParameter(string name, object val)
{
- if (!parameterMetadata.NamedParameterNames.Contains(name))
- {
- if (shouldIgnoredUnknownNamedParameters)//just ignore it
- return this;
- }
-
- if (val == null)
- {
- IType type = parameterMetadata.GetNamedParameterExpectedType(name);
- if (type == null)
- {
- throw new ArgumentNullException("val",
- "A type specific Set(name, val) should be called because the Type can not be guessed from a null value.");
- }
-
- SetParameter(name, val, type);
- }
- else
- {
- SetParameter(name, val, DetermineType(name, val));
- }
-
- return this;
+ return SetParameter(name, val, null, true);
}
public IQuery SetParameter(int position, object val)
diff --git a/src/NHibernate/Linq/DefaultQueryProvider.cs b/src/NHibernate/Linq/DefaultQueryProvider.cs
index 912b640a951..cb1e84c41a6 100644
--- a/src/NHibernate/Linq/DefaultQueryProvider.cs
+++ b/src/NHibernate/Linq/DefaultQueryProvider.cs
@@ -265,7 +265,7 @@ private static void SetParameters(IQuery query, IDictionary