Skip to content

Commit ec86c45

Browse files
Fix IFilter.SetParameterList not supporting HashSet<T> arguments.
Fixes #1585
1 parent 7ac19f3 commit ec86c45

File tree

9 files changed

+137
-13
lines changed

9 files changed

+137
-13
lines changed

src/NHibernate.Test/Async/FilterTest/DynamicFilterTest.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,35 @@ public async Task InStyleFilterParameterAsync()
366366
}
367367
}
368368

369+
[Test]
370+
public void InStyleFilterParameterWithHashSetAsync()
371+
{
372+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
373+
// one-to-many loading tests
374+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
375+
log.Info("Starting one-to-many collection loader filter tests with HashSet.");
376+
using (var session = OpenSession())
377+
{
378+
Assert.Multiple(
379+
async () =>
380+
{
381+
session.EnableFilter("regionlist")
382+
.SetParameterList("regions", new HashSet<string> { "LA", "APAC" });
383+
384+
log.Debug("Performing query of Salespersons");
385+
var salespersons = await (session.CreateQuery("from Salesperson").ListAsync());
386+
Assert.That(salespersons.Count, Is.EqualTo(1), "Incorrect salesperson count");
387+
388+
session.EnableFilter("guidlist")
389+
.SetParameterList("guids", new HashSet<Guid> { testData.Product1Guid, testData.Product2Guid });
390+
391+
log.Debug("Performing query of Products");
392+
var products = await (session.CreateQuery("from Product").ListAsync());
393+
Assert.That(products.Count, Is.EqualTo(2), "Incorrect product count");
394+
});
395+
}
396+
}
397+
369398
[Test]
370399
public async Task ManyToManyFilterOnCriteriaAsync()
371400
{
@@ -572,6 +601,8 @@ private class TestData
572601
public DateTime nextMonth;
573602
public DateTime sixMonthsAgo;
574603
public DateTime fourMonthsAgo;
604+
public Guid Product1Guid;
605+
public Guid Product2Guid;
575606

576607
private DynamicFilterTestAsync outer;
577608

@@ -631,6 +662,8 @@ public TestData(DynamicFilterTestAsync outer)
631662
product1.StockNumber = (123);
632663
product1.EffectiveStartDate = (lastMonth);
633664
product1.EffectiveEndDate = (nextMonth);
665+
product1.ProductGuid = Guid.NewGuid();
666+
Product1Guid = product1.ProductGuid;
634667

635668
product1.AddCategory(cat1);
636669
product1.AddCategory(cat2);
@@ -655,6 +688,8 @@ public TestData(DynamicFilterTestAsync outer)
655688
product2.StockNumber = (124);
656689
product2.EffectiveStartDate = (sixMonthsAgo);
657690
product2.EffectiveEndDate = (DateTime.Today);
691+
product2.ProductGuid = Guid.NewGuid();
692+
Product2Guid = product2.ProductGuid;
658693

659694
Category cat3 = new Category("test cat 2", sixMonthsAgo, DateTime.Today);
660695
product2.AddCategory(cat3);
@@ -731,6 +766,8 @@ public void Prepare()
731766
product1.StockNumber = (123);
732767
product1.EffectiveStartDate = (lastMonth);
733768
product1.EffectiveEndDate = (nextMonth);
769+
product1.ProductGuid = Guid.NewGuid();
770+
Product1Guid = product1.ProductGuid;
734771

735772
product1.AddCategory(cat1);
736773
product1.AddCategory(cat2);
@@ -755,6 +792,8 @@ public void Prepare()
755792
product2.StockNumber = (124);
756793
product2.EffectiveStartDate = (sixMonthsAgo);
757794
product2.EffectiveEndDate = (DateTime.Today);
795+
product2.ProductGuid = Guid.NewGuid();
796+
Product2Guid = product2.ProductGuid;
758797

759798
Category cat3 = new Category("test cat 2", sixMonthsAgo, DateTime.Today);
760799
product2.AddCategory(cat3);

src/NHibernate.Test/FilterTest/DynamicFilterTest.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,35 @@ public void InStyleFilterParameter()
354354
}
355355
}
356356

357+
[Test]
358+
public void InStyleFilterParameterWithHashSet()
359+
{
360+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
361+
// one-to-many loading tests
362+
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
363+
log.Info("Starting one-to-many collection loader filter tests with HashSet.");
364+
using (var session = OpenSession())
365+
{
366+
Assert.Multiple(
367+
() =>
368+
{
369+
session.EnableFilter("regionlist")
370+
.SetParameterList("regions", new HashSet<string> { "LA", "APAC" });
371+
372+
log.Debug("Performing query of Salespersons");
373+
var salespersons = session.CreateQuery("from Salesperson").List();
374+
Assert.That(salespersons.Count, Is.EqualTo(1), "Incorrect salesperson count");
375+
376+
session.EnableFilter("guidlist")
377+
.SetParameterList("guids", new HashSet<Guid> { testData.Product1Guid, testData.Product2Guid });
378+
379+
log.Debug("Performing query of Products");
380+
var products = session.CreateQuery("from Product").List();
381+
Assert.That(products.Count, Is.EqualTo(2), "Incorrect product count");
382+
});
383+
}
384+
}
385+
357386
[Test]
358387
public void ManyToManyFilterOnCriteria()
359388
{
@@ -560,6 +589,8 @@ private class TestData
560589
public DateTime nextMonth;
561590
public DateTime sixMonthsAgo;
562591
public DateTime fourMonthsAgo;
592+
public Guid Product1Guid;
593+
public Guid Product2Guid;
563594

564595
private DynamicFilterTest outer;
565596

@@ -619,6 +650,8 @@ public void Prepare()
619650
product1.StockNumber = (123);
620651
product1.EffectiveStartDate = (lastMonth);
621652
product1.EffectiveEndDate = (nextMonth);
653+
product1.ProductGuid = Guid.NewGuid();
654+
Product1Guid = product1.ProductGuid;
622655

623656
product1.AddCategory(cat1);
624657
product1.AddCategory(cat2);
@@ -643,6 +676,8 @@ public void Prepare()
643676
product2.StockNumber = (124);
644677
product2.EffectiveStartDate = (sixMonthsAgo);
645678
product2.EffectiveEndDate = (DateTime.Today);
679+
product2.ProductGuid = Guid.NewGuid();
680+
Product2Guid = product2.ProductGuid;
646681

647682
Category cat3 = new Category("test cat 2", sixMonthsAgo, DateTime.Today);
648683
product2.AddCategory(cat3);

src/NHibernate.Test/FilterTest/Product.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public virtual ISet<Category> Categories
7575
set { categories = value; }
7676
}
7777

78+
public virtual Guid ProductGuid { get; set; }
79+
7880
public override int GetHashCode()
7981
{
8082
return stockNumber;

src/NHibernate.Test/FilterTest/classes.hbm.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
<property name="EffectiveStartDate" column="eff_start_dt" type="Date"/>
8080
<property name="EffectiveEndDate" column="eff_end_dt" type="Date"/>
8181

82+
<property name="ProductGuid"/>
83+
8284
<set cascade="none" inverse="true" name="OrderLineItems">
8385
<key column="PROD_ID"/>
8486
<one-to-many class="LineItem"/>
@@ -93,6 +95,7 @@
9395
</set>
9496

9597
<filter name="effectiveDate" condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
98+
<filter name="guidlist" condition="ProductGuid IN (:guids)"/>
9699
</class>
97100

98101
<class name="Salesperson" table="SALES_PERSON">
@@ -118,4 +121,4 @@
118121
<filter name="regionlist" condition="REG IN (:regions)"/>
119122
</class>
120123

121-
</hibernate-mapping>
124+
</hibernate-mapping>

src/NHibernate.Test/FilterTest/defs.hbm.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@
2424
<filter-def name="cat">
2525
<filter-param name="catId" type="long"/>
2626
</filter-def>
27-
</hibernate-mapping>
27+
28+
<filter-def name="guidlist">
29+
<filter-param name="guids" type="guid"/>
30+
</filter-def>
31+
</hibernate-mapping>

src/NHibernate/Engine/Query/NativeSQLQueryPlan.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,19 +151,18 @@ private SqlString ExpandDynamicFilterParameters(SqlString sqlString, ICollection
151151
string parameterName = parts[1];
152152
var filter = (FilterImpl)enabledFilters[filterName];
153153

154-
object value = filter.GetParameter(parameterName);
154+
var listValue = filter.GetParameterList(parameterName);
155155
IType type = filter.FilterDefinition.GetParameterType(parameterName);
156156
int parameterColumnSpan = type.GetColumnSpan(session.Factory);
157-
var collectionValue = value as ICollection;
158157
int? collectionSpan = null;
159158

160159
// Add query chunk
161160
string typeBindFragment = string.Join(", ", Enumerable.Repeat("?", parameterColumnSpan).ToArray());
162161
string bindFragment;
163-
if (collectionValue != null && !type.ReturnedClass.IsArray)
162+
if (listValue != null && !type.ReturnedClass.IsArray)
164163
{
165-
collectionSpan = collectionValue.Count;
166-
bindFragment = string.Join(", ", Enumerable.Repeat(typeBindFragment, collectionValue.Count).ToArray());
164+
collectionSpan = listValue.Count();
165+
bindFragment = string.Join(", ", Enumerable.Repeat(typeBindFragment, collectionSpan.Value).ToArray());
167166
}
168167
else
169168
{

src/NHibernate/Impl/FilterImpl.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using NHibernate.Engine;
34
using NHibernate.Type;
45
using System.Collections.Generic;
@@ -16,6 +17,7 @@ public class FilterImpl : IFilter
1617
private FilterDefinition definition;
1718

1819
private readonly IDictionary<string, object> parameters = new Dictionary<string, object>();
20+
private readonly IDictionary<string, IEnumerable> _parameterLists = new Dictionary<string, IEnumerable>();
1921

2022
public void AfterDeserialize(FilterDefinition factoryDefinition)
2123
{
@@ -75,6 +77,7 @@ public IFilter SetParameter(string name, object value)
7577
/// <param name="name">The parameter's name.</param>
7678
/// <param name="values">The values to be expanded into an SQL IN list.</param>
7779
/// <returns>This FilterImpl instance (for method chaining).</returns>
80+
/// <exception cref="ArgumentNullException">Thrown when <paramref name="name"/> or <paramref name="values"/> are <see langword="null" />.</exception>
7881
public IFilter SetParameterList<T>(string name, ICollection<T> values)
7982
{
8083
var type = definition.GetParameterType(name);
@@ -88,7 +91,10 @@ public IFilter SetParameterList<T>(string name, ICollection<T> values)
8891
throw new HibernateException("Incorrect type for parameter [" + name + "]");
8992
}
9093

91-
parameters[name] = values ?? throw new ArgumentException("Collection must be not null!", nameof(values));
94+
_parameterLists[name] = values ??
95+
// This guarantees GetParameterList semantic.
96+
throw new ArgumentNullException(nameof(values), "Collection must be not null!");
97+
parameters[name] = values;
9298
return this;
9399
}
94100

@@ -99,6 +105,17 @@ public object GetParameter(string name)
99105
return result;
100106
}
101107

108+
/// <summary>
109+
/// Get a parameter list by name. <see langword="null" /> if there is no parameter list for that name.
110+
/// </summary>
111+
/// <param name="name">The parameter name.</param>
112+
/// <returns>The parameter list, or <see langword="null" /> if there is no parameter list for that name.</returns>
113+
public IEnumerable GetParameterList(string name)
114+
{
115+
_parameterLists.TryGetValue(name, out var result);
116+
return result;
117+
}
118+
102119
/// <summary>
103120
/// Perform validation of the filter state. This is used to verify the
104121
/// state of the filter after its enablement and before its use.

src/NHibernate/Loader/Loader.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,19 +1801,18 @@ protected SqlString ExpandDynamicFilterParameters(SqlString sqlString, ICollecti
18011801
string parameterName = parts[1];
18021802
var filter = (FilterImpl)enabledFilters[filterName];
18031803

1804-
object value = filter.GetParameter(parameterName);
1804+
var listValue = filter.GetParameterList(parameterName);
18051805
IType type = filter.FilterDefinition.GetParameterType(parameterName);
18061806
int parameterColumnSpan = type.GetColumnSpan(session.Factory);
1807-
var collectionValue = value as ICollection;
18081807
int? collectionSpan = null;
18091808

18101809
// Add query chunk
18111810
string typeBindFragment = string.Join(", ", Enumerable.Repeat("?", parameterColumnSpan).ToArray());
18121811
string bindFragment;
1813-
if (collectionValue != null && !type.ReturnedClass.IsArray)
1812+
if (listValue != null && !type.ReturnedClass.IsArray)
18141813
{
1815-
collectionSpan = collectionValue.Count;
1816-
bindFragment = string.Join(", ", Enumerable.Repeat(typeBindFragment, collectionValue.Count).ToArray());
1814+
collectionSpan = listValue.Count();
1815+
bindFragment = string.Join(", ", Enumerable.Repeat(typeBindFragment, collectionSpan.Value).ToArray());
18171816
}
18181817
else
18191818
{

src/NHibernate/Util/EnumerableExt.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections;
3+
4+
namespace NHibernate.Util
5+
{
6+
internal static class EnumerableExt
7+
{
8+
internal static int Count(this IEnumerable source)
9+
{
10+
if (source is ICollection col)
11+
return col.Count;
12+
13+
var count = 0;
14+
var e = source.GetEnumerator();
15+
//in case it's generic disposable enumerator
16+
using (e as IDisposable)
17+
{
18+
while (e.MoveNext())
19+
{
20+
count++;
21+
}
22+
}
23+
return count;
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)