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