ExpressionParameterVisitor selects wrong CustomType for ConstantExpression (Linq)

Description

We have a legacy oracle database that uses double values as primary keys. In order to convert those double values to culture-invariant strings we are using a custom "DoubleStringUserType" (the returned type is "typeof(string)").

We have tried to upgrade from NHibernate 4.0.4 to 4.1.0 and now some queries fail because NHibernate 4.1.0 takes this custom DoubleStringUserType for all string constants in the expression.

The trouble is caused by the code introduced in the attached commit:
The Method VisitConstantExpression (ExpressionParameterVisitor) takes the first custom type it finds where the ReturnedType is assignable to the expression.

A simple expression like the following now fails:
repository.Where(p => p.Employee.Id == employeeId && pAddress.Tags.Any(v => v.Keyword== "Employee"))

because the ExpressionParameterVisitor in NHibernate 4.1 takes the constant expression "Employee" of type string and the visitor finds the first custom type assignable to string (which happens to be our DoubleStringUserType). So eventually NHibernate now tries to convert the string "Employee" to a double (using our custom user type) and this causes an invalid cast exception (for obvious reasons).

I've cloned the NHibernate repo (commit 36100cc0a31f85e2364171839904575fb649473f) and commented out the following lines and then our queries start to work again:

if (type == null) { var customType = _allMappedCustomTypes.FirstOrDefault(ct => ct.UserType.ReturnedType.IsAssignableFrom(expression.Type)); if (customType != null) { type = customType; } }

The code snippet is taken from the fix for this issue:
https://nhibernate.jira.com/browse/NH-3904

I hope that my problem description is detailed enough (if you need more infos let me know).

Environment

None

Attachments

2
  • 11 Jan 2017, 11:40 AM
  • 05 Jan 2017, 02:10 PM

Activity

Show:

Petr Waclawek January 30, 2017 at 3:21 PM
Edited

I have experienced the same problem with a custom user type serializing an object as an XML.

May be the issue can be solved by limiting valid custom return types, e.g.:

var customType = _allMappedCustomTypes.FirstOrDefault( ct => !ct.UserType.ReturnedType.IsPrimitive && ct.UserType.ReturnedType != typeof(string) && ct.UserType.ReturnedType != typeof(object) && ct.UserType.ReturnedType.IsAssignableFrom(expression.Type));

Stefan Steinegger January 13, 2017 at 2:47 PM
Edited

Hi all,

we are facing the exact same problem. We have a custom type which returns a string. It is now used for all constant string parameters of linq queries, even when completely unrelated to the filtered column. This is a show stopper for us to upgrade to 4.1 (we are still on 3.3. because of another show stopper in 4.0 which seems to be resolved now).

Immediately before the code you pasted which chooses the custom type, there is following code:

(ExpressionParameterVisitor line 94)

if (expression.Value == null) type = NHibernateUtil.GuessType(expression.Type);

When I skip the "if" line in the debugger and execute GuessType, it chooses the correct type. I assume that the if condition is wrong. Or probably not required at all.

Former user January 13, 2017 at 9:54 AM

Hi Sammi,

Sorry if my posts are not written clear enough/confusing.

What I was trying to explain is that finding the first matching custom type is what NHibernate does since version 4.1.0 and this is what makes our queries fail.
The attached test case demonstrates this. So from my point of view this is a bug (and this is also the reason why we can't upgrade from 4.0.4 to 4.1.0).

Selecting the first matching custom type was introduced in NH version 4.1.0 (part of the bugfix for NH-3904|https://nhibernate.jira.com/browse/NH-3904]).

I am not part of the NHibernate development team and I don't know the reasoning behind the decision to take the first custom type.

Regards,

Marcel

Sammi Maan Sinno January 12, 2017 at 11:53 PM

I'm having a similar issue, and what you have written is a bit confusing to me. Why would it try to find the first matching custom type? That sounds like an NH bug to me. How exactly should we fix this?

Former user January 11, 2017 at 11:55 AM

Hi Alexander,

I have uploaded a test to reproduce this issue (NH3929.zip). If you put it in the NHSpecificTest folder (solution NHibernate.Test) and run the test it should fail. By commenting out the section as stated in my first post the test succeeds.

My observations that might help you:

  • the class Person in the test has a property "LastModifiedByUserId" which is mapped using our DoubleStringUserType

  • the ExpressionTreeVisitor constructor fills the collection "_allMappedCustomTypes"

  • When querying for p.LastName == "Foo" the expressionTreeVisitor finds the first matching custom type (= DoubleStringUserType) and tries to apply this custom type to the ConstantExpression "Foo" and this eventually causes the test to fail

Another thing that I have noticed: if you just map the id with a custom type (and don't have any other property mapped with a custom type - i.e. if you remove the LastModifiedByUserId property from the person class (and its mapping)) the problem does not occur. The constructor of the ExpressiontreeVisitor class just searches for properties of customtype. If only the id column is mapped with a custom type and all other properties are mapped with the standard types the "_allMappedCustomTypes" collection is empty and the test succeeds, too.

I hope that this helps fixing the bug. If you have any further questions just let me know.

Regards,

Marcel

Fixed

Details

Assignee

Reporter

Labels

Components

Fix versions

Affects versions

Priority

Who's Looking?

Open Who's Looking?
Created January 5, 2017 at 2:49 PM
Updated February 2, 2017 at 1:11 AM
Resolved February 1, 2017 at 10:53 PM
Who's Looking?