MultiQuery/ToFuture broken with Contains (in)
Description
Environment
is duplicated by
is related to
testing discovered
Activity
It seems not
Will this be included in 3.3.1 GA?
Ok, I've found shorter way to fix this issue.
Fix commited to master 92ba8462db29e5b1d547d3b8701dddede558f927
Here is a work around I created in case it helps any one:
using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate;
using NHibernate.Linq;
using System.Collections.Generic;
public static class NHExtensions
{
public static IQueryable<TSource> AppendContains<TSource, TResult>(this IQueryable<TSource> source,
Expression<Func<TSource, TResult>> selector,
IEnumerable<TResult> options)
{
if (!options.Any())
throw new ArgumentException("At least one options must be specified.", "options");
Expression orExpression = null;
Expression selectorExpression = (MemberExpression)selector.Body;
foreach (var item in options)
{
Expression equalExpression = Expression.Equal(selectorExpression, Expression.Constant(item));
orExpression = orExpression == null ? equalExpression :
Expression.OrElse(orExpression, equalExpression);
}
var funcType = Expression.GetFuncType(typeof(TSource), typeof(bool));
var sourceParam = Expression.Parameter(typeof(TSource), "source");
var whereClause = (Expression<Func<TSource, bool>>)Expression.Lambda(funcType, orExpression, sourceParam);
return source.Where(whereClause);
}
}
Here is an example usage:
var users = (from u in session.Query<Users>()
where u.UserType == "Admin"
select u).AppendContains(u => user.UserId, new[]{ 1, 2, 3 });
This will generate SQL similar to SELECT ... FROM User WHERE UserType = 'Admin' AND (UserId = 1 OR UserId = 3 or UserId = 3)
I've only tested this in very limited circumstances, so use at your own risk. The downsides are that this isn't as fluent as using .Contains(), and that if you have F future queries that depend on P options, then you end up with F*P total parameters to the query (not including any other parameters that are necessary) so the number of parameters can increase quickly if you aren't careful.
Hope it helps a few people until a real fix is in place.
Chris
@Alexander I. Zaytsev: Thanks! I will continue watching this issue as I imagine it will be marked as resolved when 3.4 ships?
Details
Details
Assignee
Reporter

There is an error in NamedParameterSpecification handling when using multiqueries or linq .ToFuture() with a query having an "in" clause.
Here's a failing unit test, put it in LinqFutureFixture.cs:
[Test]
public void CanUseToFutureWithContains()
{
using (var s = sessions.OpenSession())
{
var ids = new[] { 1, 2, 3 };
var persons10 = s.Query<Person>()
.Where(p => ids.Contains(p.Id))
.FetchMany(p => p.Children)
.Skip(5)
.Take(10)
.ToFuture().ToList();
Assert.Pass();
}
}
Stack trace:
System.Collections.Generic.KeyNotFoundException : The given key was not present in the dictionary.
at System.ThrowHelper.ThrowKeyNotFoundException()
at System.Collections.Generic.Dictionary`2.get_Item(TKey key)
at NHibernate.Param.NamedParameterSpecification.SetEffectiveType(QueryParameters queryParameters) in NamedParameterSpecification.cs: line 70
at NHibernate.Param.ParametersBackTrackExtensions.ResetEffectiveExpectedType(IEnumerable`1 parameterSpecs, QueryParameters queryParameters) in ParametersBackTrackExtensions.cs: line 48
at NHibernate.Hql.Ast.ANTLR.Loader.QueryLoader.ResetEffectiveExpectedType(IEnumerable`1 parameterSpecs, QueryParameters queryParameters) in QueryLoader.cs: line 428
at NHibernate.Loader.Loader.CreateSqlCommand(QueryParameters queryParameters, ISessionImplementor session) in Loader.cs: line 1649
at NHibernate.Impl.MultiQueryImpl.AggregateQueriesInformation() in MultiQueryImpl.cs: line 641
at NHibernate.Impl.MultiQueryImpl.get_Parameters() in MultiQueryImpl.cs: line 774
at NHibernate.Impl.MultiQueryImpl.CreateCombinedQueryParameters() in MultiQueryImpl.cs: line 754
at NHibernate.Impl.MultiQueryImpl.List() in MultiQueryImpl.cs: line 400
at NHibernate.Impl.FutureQueryBatch.GetResultsFrom(IMultiQuery multiApproach) in FutureQueryBatch.cs: line 24
at NHibernate.Impl.FutureBatch`2.GetResults() in FutureBatch.cs: line 73
at NHibernate.Impl.FutureBatch`2.get_Results() in FutureBatch.cs: line 29
at NHibernate.Impl.FutureBatch`2.GetCurrentResult(Int32 currentIndex) in FutureBatch.cs: line 79
at NHibernate.Impl.FutureBatch`2.<>c_DisplayClass4`1.<GetEnumerator>b_3() in FutureBatch.cs: line 63
at NHibernate.Impl.DelayedEnumerator`1.<get_Enumerable>d__0.MoveNext() in DelayedEnumerator.cs: line 26
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList(IEnumerable`1 source)
at NHibernate.Test.NHSpecificTest.Futures.LinqFutureFixture.CanUseToFutureWithContains