From 680d40f13f719dc5e34d89bc94b6dec6e1bbe02d Mon Sep 17 00:00:00 2001 From: Oskar Berggren Date: Sun, 4 Dec 2016 15:16:22 +0100 Subject: [PATCH 1/3] LoggingTests: Fix test brittleness. (cherry picked from commit a2e4891ee658ce8148e6554918a133e857acfb44) --- src/NHibernate.Test/Linq/LoggingTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NHibernate.Test/Linq/LoggingTests.cs b/src/NHibernate.Test/Linq/LoggingTests.cs index 0df9167a621..a42e7aa5eef 100644 --- a/src/NHibernate.Test/Linq/LoggingTests.cs +++ b/src/NHibernate.Test/Linq/LoggingTests.cs @@ -47,9 +47,9 @@ public void CanLogLinqExpressionWithoutInitializingContainedProxy() // Verify that the expected logging did happen. var actualLog = logspy.GetWholeLog(); - const string expectedLog = - "Expression (partially evaluated): value(NHibernate.Linq.NhQueryable`1[NHibernate.DomainModel.Northwind.Entities.Product])" + - ".Where(product => (product == Product#1)).Count()"; + string expectedLog = + "Expression (partially evaluated): value(NHibernate.Linq.NhQueryable`1[NHibernate.DomainModel.Northwind.Entities.Product])" + + ".Where(product => (product == Product#" + productId + ")).Count()"; Assert.That(actualLog, Is.StringContaining(expectedLog)); // And verify that the proxy in the expression wasn't initialized. From 9c92e0f881c356ff330348f28ff9e5698650acd2 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 10 Jan 2017 14:39:26 +1300 Subject: [PATCH 2/3] NH-3929 - Revert NH-3904 * Users required to explicitly specify custom user type via MappedAs method if they want to use IUserType/ICompositeUserType type parameters in Linq queries with generators custom generators, or properly implement generators to take the type into account. * Fix MappedAs method (NH-2401). --- .../DoubleStringUserType.cs | 82 +++++++++++++++++++ .../EntityWithUserTypeProperty.cs | 1 + .../Fixture.cs | 50 ++++++++++- .../Mappings.hbm.xml | 3 +- src/NHibernate.Test/NHibernate.Test.csproj | 1 + src/NHibernate/Linq/NhLinqExpression.cs | 5 +- .../Visitors/ExpressionParameterVisitor.cs | 30 +++---- 7 files changed, 144 insertions(+), 28 deletions(-) create mode 100644 src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/DoubleStringUserType.cs diff --git a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/DoubleStringUserType.cs b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/DoubleStringUserType.cs new file mode 100644 index 00000000000..2b3b7c26dbd --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/DoubleStringUserType.cs @@ -0,0 +1,82 @@ +using System; +using System.Data; +using NHibernate.SqlTypes; +using NHibernate.UserTypes; + +namespace NHibernate.Test.NHSpecificTest.EntityWithUserTypeCanHaveLinqGenerators +{ + public class DoubleStringUserType : IUserType + { + public SqlType[] SqlTypes + { + get { return new[] { SqlTypeFactory.GetString(20) }; } + } + + public System.Type ReturnedType + { + get { return typeof(string); } + } + + public bool IsMutable + { + get { return false; } + } + + public int GetHashCode(object x) + { + if (x == null) + { + return 0; + } + return x.GetHashCode(); + } + + public object NullSafeGet(IDataReader rs, string[] names, object owner) + { + object obj = NHibernateUtil.String.NullSafeGet(rs, names[0]); + if (obj == null) + { + return null; + } + return Convert.ToDouble((string)obj); + } + + public void NullSafeSet(IDbCommand cmd, object value, int index) + { + if (value == null) + { + ((IDataParameter)cmd.Parameters[index]).Value = DBNull.Value; + } + else + { + var doubleValue = (double)value; + ((IDataParameter)cmd.Parameters[index]).Value = doubleValue.ToString(); + } + } + + public object DeepCopy(object value) + { + return value; + } + + public object Replace(object original, object target, object owner) + { + return original; + } + + public object Assemble(object cached, object owner) + { + return cached; + } + + public object Disassemble(object value) + { + return value; + } + + bool IUserType.Equals(object x, object y) + { + return object.Equals(x, y); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/EntityWithUserTypeProperty.cs b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/EntityWithUserTypeProperty.cs index dcb40dc8210..63138e0a3af 100644 --- a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/EntityWithUserTypeProperty.cs +++ b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/EntityWithUserTypeProperty.cs @@ -6,6 +6,7 @@ public class EntityWithUserTypeProperty public virtual int Id { get; set; } public virtual string Name { get; set; } public virtual IExample Example { get; set; } + public virtual double DoubleStoredAsString { get; set; } } diff --git a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Fixture.cs b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Fixture.cs index 21d0d10e025..37b51731c50 100644 --- a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Fixture.cs +++ b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Fixture.cs @@ -4,7 +4,6 @@ using NHibernate.Linq; using NUnit.Framework; - namespace NHibernate.Test.NHSpecificTest.EntityWithUserTypeCanHaveLinqGenerators { @@ -71,7 +70,6 @@ protected override void OnTearDown() } } - [Test] public void EqualityWorksForUserType() { @@ -80,14 +78,14 @@ public void EqualityWorksForUserType() { var newItem = new BarExample { Value = "Larry" }; var entities = session.Query() - .Where(x=>x.Example == newItem) + .Where(x => x.Example == newItem) .ToList(); Assert.AreEqual(1, entities.Count); } } - [Test] + [Test, Ignore("Not implemented yet")] public void LinqMethodWorksForUserType() { using (var session = OpenSession()) @@ -102,6 +100,50 @@ public void LinqMethodWorksForUserType() } } + [Test] + public void EqualityWorksForExplicitUserType() + { + using (var session = OpenSession()) + using (session.BeginTransaction()) + { + var newItem = new BarExample { Value = "Larry" }; + var entities = session.Query() + .Where(x => x.Example == newItem.MappedAs(NHibernateUtil.Custom(typeof(ExampleUserType)))) + .ToList(); + + Assert.AreEqual(1, entities.Count); + } + } + + [Test] + public void LinqMethodWorksForExplicitUserType() + { + using (var session = OpenSession()) + using (session.BeginTransaction()) + { + var newItem = new BarExample { Value = "Larry" }; + var entities = session.Query() + .Where(x => x.Example.IsEquivalentTo(newItem.MappedAs(NHibernateUtil.Custom(typeof(ExampleUserType))))) + .ToList(); + + Assert.AreEqual(2, entities.Count); + } + } + + [Test] + public void LinqMethodWorksForStandardStringProperty() + { + using (var session = OpenSession()) + using (session.BeginTransaction()) + { + var entities = session.Query() + .Where(x => x.Name == "Bob") + .ToList(); + + Assert.AreEqual(1, entities.Count); + } + } + [Test] public void CanQueryWithHql() { diff --git a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Mappings.hbm.xml b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Mappings.hbm.xml index fd48132a68b..7f98c75e81e 100644 --- a/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Mappings.hbm.xml +++ b/src/NHibernate.Test/NHSpecificTest/EntityWithUserTypeCanHaveLinqGenerators/Mappings.hbm.xml @@ -6,5 +6,6 @@ + - \ No newline at end of file + diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index 87f2658c28b..3d15ec6e935 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -725,6 +725,7 @@ + diff --git a/src/NHibernate/Linq/NhLinqExpression.cs b/src/NHibernate/Linq/NhLinqExpression.cs index bcff12b5ea5..d7c671ced7d 100644 --- a/src/NHibernate/Linq/NhLinqExpression.cs +++ b/src/NHibernate/Linq/NhLinqExpression.cs @@ -31,14 +31,13 @@ public class NhLinqExpression : IQueryExpression public NhLinqExpression(Expression expression, ISessionFactoryImplementor sessionFactory) { _expression = NhPartialEvaluatingExpressionTreeVisitor.EvaluateIndependentSubtrees(expression); - // We want logging to be as close as possible to the original expression sent from the // application. But if we log before partial evaluation, the log won't include e.g. // subquery expressions if those are defined by the application in a variable referenced // from the main query. LinqLogging.LogExpression("Expression (partially evaluated)", _expression); - _constantToParameterMap = ExpressionParameterVisitor.Visit(_expression, sessionFactory); + _constantToParameterMap = ExpressionParameterVisitor.Visit(ref _expression, sessionFactory); ParameterValuesByName = _constantToParameterMap.Values.ToDictionary(p => p.Name, p => System.Tuple.Create(p.Value, p.Type)); @@ -77,4 +76,4 @@ internal void CopyExpressionTranslation(NhLinqExpression other) ParameterDescriptors = other.ParameterDescriptors; } } -} \ No newline at end of file +} diff --git a/src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs b/src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs index ec543e9af48..b43a9d844d6 100644 --- a/src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs +++ b/src/NHibernate/Linq/Visitors/ExpressionParameterVisitor.cs @@ -26,21 +26,21 @@ public class ExpressionParameterVisitor : ExpressionTreeVisitor ReflectionHelper.GetMethodDefinition(() => Enumerable.Take(null, 0)), }; - private readonly List _allMappedCustomTypes; - public ExpressionParameterVisitor(ISessionFactoryImplementor sessionFactory) { _sessionFactory = sessionFactory; - _allMappedCustomTypes = _sessionFactory.GetAllClassMetadata().Values - .SelectMany(c => c.PropertyTypes) - .OfType().ToList(); } public static IDictionary Visit(Expression expression, ISessionFactoryImplementor sessionFactory) + { + return Visit(ref expression, sessionFactory); + } + + internal static IDictionary Visit(ref Expression expression, ISessionFactoryImplementor sessionFactory) { var visitor = new ExpressionParameterVisitor(sessionFactory); - - visitor.VisitExpression(expression); + + expression = visitor.VisitExpression(expression); return visitor._parameters; } @@ -49,10 +49,10 @@ protected override Expression VisitMethodCallExpression(MethodCallExpression exp { if (expression.Method.Name == "MappedAs" && expression.Method.DeclaringType == typeof(LinqExtensionMethods)) { - var parameter = (ConstantExpression) VisitExpression(expression.Arguments[0]); - var type = (ConstantExpression) expression.Arguments[1]; + var parameter = (ConstantExpression)VisitExpression(expression.Arguments[0]); + var type = (ConstantExpression)expression.Arguments[1]; - _parameters[parameter].Type = (IType) type.Value; + _parameters[parameter].Type = (IType)type.Value; return parameter; } @@ -94,16 +94,6 @@ protected override Expression VisitConstantExpression(ConstantExpression express if (expression.Value == null) type = NHibernateUtil.GuessType(expression.Type); - if (type == null) - { - var customType = - _allMappedCustomTypes.FirstOrDefault(ct => ct.UserType.ReturnedType.IsAssignableFrom(expression.Type)); - if (customType != null) - { - type = customType; - } - } - // Constant characters should be sent as strings if (expression.Type == typeof(char)) { From b64b6b74278a12a6a12e19e6a33f97fac6b6c910 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Thu, 2 Feb 2017 12:07:26 +1300 Subject: [PATCH 3/3] Prepare 4.1.1.GA release. * Assembly version stays the same --- build-common/common.xml | 2 +- releasenotes.txt | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/build-common/common.xml b/build-common/common.xml index fed321120d6..c57395e8791 100644 --- a/build-common/common.xml +++ b/build-common/common.xml @@ -52,7 +52,7 @@ effectively SP0). --> - + diff --git a/releasenotes.txt b/releasenotes.txt index 0bf29965b74..9d994357dbd 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -1,3 +1,18 @@ +Build 4.1.1.GA +============================= + + ##### Notes ##### + The [NH-3904] has been reverted in favor of [NH-2401]: users now required to explicitly specify + custom user type via MappedAs method if they want to use IUserType/ICompositeUserType type + parameters in Linq queries, or implement generators the way they take the types into account. + +** Sub-task + * [NH-3940] - Revert NH-3904 + +** Bug + * [NH-3929] - ExpressionParameterVisitor selects wrong CustomType for ConstantExpression (Linq) + * [NH-3941] - MappedAs() does not work + Build 4.1.0.GA =============================