Skip to content

Commit 5e30f86

Browse files
Support custom row key comparers (allows for case sensitivity control, performance tuning, etc.)
1 parent 401885c commit 5e30f86

File tree

3 files changed

+79
-9
lines changed

3 files changed

+79
-9
lines changed

Rhino.Etl.Core/QuackingDictionary.cs

+34-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ namespace Rhino.Etl.Core
1414
[Serializable]
1515
public class QuackingDictionary : IDictionary, IQuackFu
1616
{
17+
/// <summary>
18+
/// Default value for the Comparer property.
19+
/// Defines how keys are compared (case sensitivity, hashing algorithm, etc.)
20+
/// </summary>
21+
public static StringComparer DefaultComparer { get; set; }
22+
23+
/// <summary>
24+
/// Initialization for static members/properties
25+
/// </summary>
26+
static QuackingDictionary()
27+
{
28+
DefaultComparer = StringComparer.InvariantCultureIgnoreCase;
29+
}
30+
1731
/// <summary>
1832
/// The inner items collection
1933
/// </summary>
@@ -26,6 +40,11 @@ public class QuackingDictionary : IDictionary, IQuackFu
2640

2741
private bool throwOnMissing = false;
2842

43+
/// <summary>
44+
/// Defines how keys are compared (case sensitivity, hashing algorithm, etc.)
45+
/// </summary>
46+
protected StringComparer Comparer { get; set; }
47+
2948
/// <summary>
3049
/// Set the flag to thorw if key not found.
3150
/// </summary>
@@ -38,12 +57,24 @@ public void ShouldThorwIfKeyNotFound()
3857
/// Initializes a new instance of the <see cref="QuackingDictionary"/> class.
3958
/// </summary>
4059
/// <param name="items">The items.</param>
41-
public QuackingDictionary(IDictionary items)
60+
/// <param name="comparer">Defines key equality</param>
61+
public QuackingDictionary(IDictionary items, StringComparer comparer)
4262
{
63+
Comparer = comparer;
64+
4365
if (items != null)
44-
this.items = new Hashtable(items, StringComparer.InvariantCultureIgnoreCase);
66+
this.items = new Hashtable(items, Comparer);
4567
else
46-
this.items = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
68+
this.items = new Hashtable(Comparer);
69+
}
70+
71+
/// <summary>
72+
/// Initializes a new instance of the <see cref="QuackingDictionary"/> class.
73+
/// </summary>
74+
/// <param name="items">The items.</param>
75+
public QuackingDictionary(IDictionary items)
76+
: this(items, DefaultComparer)
77+
{
4778
}
4879

4980

Rhino.Etl.Core/Row.cs

+19-6
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,30 @@ public class Row : QuackingDictionary, IEquatable<Row>
2222
static readonly Dictionary<Type, List<PropertyInfo>> propertiesCache = new Dictionary<Type, List<PropertyInfo>>();
2323
static readonly Dictionary<Type, List<FieldInfo>> fieldsCache = new Dictionary<Type, List<FieldInfo>>();
2424

25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="Row"/> class.
27+
/// </summary>
28+
/// <param name="comparer">Defines key equality</param>
29+
public Row(StringComparer comparer)
30+
: base(new Hashtable(), comparer)
31+
{
32+
}
33+
2534
/// <summary>
2635
/// Initializes a new instance of the <see cref="Row"/> class.
2736
/// </summary>
2837
public Row()
29-
: base(new Hashtable(StringComparer.InvariantCultureIgnoreCase))
38+
: base(new Hashtable())
3039
{
3140
}
3241

3342
/// <summary>
3443
/// Initializes a new instance of the <see cref="Row"/> class.
3544
/// </summary>
3645
/// <param name="itemsToClone">The items to clone.</param>
37-
protected Row(IDictionary itemsToClone)
38-
: base(new Hashtable(itemsToClone, StringComparer.InvariantCultureIgnoreCase))
46+
/// <param name="comparer">Defines key equality</param>
47+
protected Row(IDictionary itemsToClone, StringComparer comparer)
48+
: base(itemsToClone, comparer)
3949
{
4050
}
4151

@@ -46,7 +56,7 @@ protected Row(IDictionary itemsToClone)
4656
/// <param name="source">The source row.</param>
4757
public void Copy(IDictionary source)
4858
{
49-
items = new Hashtable(source, StringComparer.InvariantCultureIgnoreCase);
59+
items = new Hashtable(source, Comparer);
5060
}
5161

5262
/// <summary>
@@ -72,7 +82,7 @@ public IEnumerable<string> Columns
7282
/// <returns></returns>
7383
public Row Clone()
7484
{
75-
Row row = new Row(this);
85+
Row row = new Row(this, Comparer);
7686
return row;
7787
}
7888

@@ -85,7 +95,10 @@ public Row Clone()
8595
/// <param name="other">An object to compare with this object.</param>
8696
public bool Equals(Row other)
8797
{
88-
if(Columns.SequenceEqual(other.Columns, StringComparer.InvariantCultureIgnoreCase) == false)
98+
if (!Comparer.Equals(other.Comparer))
99+
return false;
100+
101+
if(Columns.SequenceEqual(other.Columns, Comparer) == false)
89102
return false;
90103

91104
foreach (var key in items.Keys)

Rhino.Etl.Tests/RowTest.cs

+26
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,33 @@ public void Should_not_take_casing_into_account_in_column_names()
4545
Row second = new Row();
4646
second["a"] = 1;
4747

48+
Row third = second.Clone();
49+
50+
Row fourth = new Row();
51+
fourth.Copy(third);
52+
4853
Assert.True(first.Equals(second));
54+
Assert.True(first.Equals(third));
55+
Assert.True(first.Equals(fourth));
56+
}
57+
58+
[Fact]
59+
public void Should_respect_stringcomparer_specification()
60+
{
61+
Row first = new Row(StringComparer.Ordinal);
62+
first["A"] = 1;
63+
64+
Row second = new Row(StringComparer.Ordinal);
65+
second["a"] = 1;
66+
67+
Row third = second.Clone();
68+
69+
Row fourth = new Row();
70+
fourth.Copy(second);
71+
72+
Assert.False(first.Equals(second));
73+
Assert.False(first.Equals(third));
74+
Assert.False(first.Equals(fourth));
4975
}
5076

5177
[Theory]

0 commit comments

Comments
 (0)