-
Notifications
You must be signed in to change notification settings - Fork 936
Description
Invalid parameter conversion for enums mapped in sub-classes in NHibernate 5.3.
Most probably related to this issue - #2439
After upgrading to NHibernate 5.3.5 from 5.2.7 some unit tests failed with the following error:
NHibernate.Exceptions.GenericADOException : could not execute query
Data:
Severity: ERROR
InvariantSeverity: ERROR
SqlState: 42883
MessageText: operator does not exist: text = integer
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Position: 197
File: parse_oper.c
Line: 726
Routine: op_error
actual-sql-query: select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0)
After digging into the issue, we found that System.Enum
properly cast to a string value in most of the cases. Although in case when enum property is mapped not in the super-class of the persistent entity but rather in sub-classes, NHibernate fails to cast type of SQL parameter correctly.
Here is the example of such entities, their mapping and query.
public class Zoo
{
public virtual ICollection<Animal> Animals { get; } = new List<Animal>();
}
public enum HabitatEnum
{
Europe,
America,
Australia
}
public abstract class Animal
{
public virtual Zoo Zoo { get; set; }
}
public class Raccoon : Animal
{
public virtual HabitatEnum Habitat { get; set; }
}
public class ZooMapping : ClassMap<Zoo>
{
public ZooMapping()
{
Table("zoo");
HasMany(x => x.Animals)
.AsList()
.LazyLoad()
.Inverse();
}
}
public class AnimalMapping : ClassMap<Animal>
{
public AnimalMapping()
{
Table("animal");
DiscriminateSubClassesOnColumn("type").Not.Nullable();
References(x => x.Zoo)
.Column("zoo_id")
.Not.Nullable()
.LazyLoad();
}
}
public class RaccoonMapping : SubclassMap<Raccoon>
{
public RaccoonMapping()
{
DiscriminatorValue("raccoon");;
Map(x => x.Habitat)
.Column("habitat")
.Not.Nullable();
}
}
And finally the query
Session.Query<Zoo>()
.Where(zoo =>
zoo.Animals
.OfType<Raccoon>()
.Any(animal => animal.Habitat == HabitatEnum.America))
.ToList();
This example works just fine with NHibernate 5.2.7. It also works correctly if enum moved from sub-class Racoon
to the super-class Animal
in NHibernate 5.3.5.
Full stack trace is listed below:
NHibernate.Exceptions.GenericADOException : could not execute query
[ select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0) ]
Name:p1 - Value:1
[SQL: select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0)]
----> Npgsql.PostgresException : 42883: operator does not exist: text = integer
Data:
Severity: ERROR
InvariantSeverity: ERROR
SqlState: 42883
MessageText: operator does not exist: text = integer
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Position: 197
File: parse_oper.c
Line: 726
Routine: op_error
actual-sql-query: select zoo0_.id as id1_12_ from public.zoo zoo0_ where exists (select animals1_.id from public.animal animals1_ where zoo0_.id=animals1_.zoo_id and animals1_.type='raccoon' and (animals1_.habitat)=:p0)
at NHibernate.Loader.Loader.DoList(ISessionImplementor session, QueryParameters queryParameters, IResultTransformer forcedResultTransformer, QueryCacheResultBuilder queryCacheResultBuilder)
at NHibernate.Loader.Loader.ListIgnoreQueryCache(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Loader.Hql.QueryLoader.List(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Hql.Ast.ANTLR.QueryTranslatorImpl.List(ISessionImplementor session, QueryParameters queryParameters)
at NHibernate.Engine.Query.HQLQueryPlan.PerformList(QueryParameters queryParameters, ISessionImplementor session, IList results)
at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results, Object filterConnection)
at NHibernate.Impl.SessionImpl.List(IQueryExpression queryExpression, QueryParameters queryParameters, IList results)
at NHibernate.Impl.AbstractSessionImpl.List[T](IQueryExpression query, QueryParameters parameters)
at NHibernate.Impl.AbstractQueryImpl2.List[T]()
at NHibernate.Linq.DefaultQueryProvider.ExecuteList[TResult](Expression expression)
at NHibernate.Linq.NhQueryable`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
System info
- Net Framework 4.7.2
- PostgreSQL 11
- NHibernate 5.3.5
- FluentNHibernate 3.1.0
- Npgsql 5.0.1.1