Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit e6e4681

Browse files
committed
Add support for case-insensitive references in POCO References support
1 parent 04bc578 commit e6e4681

File tree

4 files changed

+140
-2
lines changed

4 files changed

+140
-2
lines changed

src/ServiceStack.OrmLite/OrmLiteConfig.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,5 +193,7 @@ public static IOrmLiteExecFilter ExecFilter
193193
/// Only supported in Oracle DialectProvider
194194
/// </summary>
195195
public static bool UseParameterizeSqlExpressions { get; set; }
196+
197+
public static bool IsCaseInsensitive { get; set; }
196198
}
197199
}

src/ServiceStack.OrmLite/Support/LoadList.cs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,42 @@ protected string GetRefFieldSql(ModelDefinition refModelDef, FieldDefinition ref
113113
return sqlRef;
114114
}
115115

116+
protected Dictionary<object, object> CreateRefMap()
117+
{
118+
return OrmLiteConfig.IsCaseInsensitive
119+
? new Dictionary<object, object>(CaseInsensitiveObjectComparer.Instance)
120+
: new Dictionary<object, object>();
121+
}
122+
123+
public class CaseInsensitiveObjectComparer : IEqualityComparer<object>
124+
{
125+
public static CaseInsensitiveObjectComparer Instance = new CaseInsensitiveObjectComparer();
126+
127+
public bool Equals(object x, object y)
128+
{
129+
if (x == null && y == null) return true;
130+
if (x == null || y == null) return false;
131+
132+
var xStr = x as string;
133+
var yStr = y as string;
134+
135+
return xStr != null && yStr != null
136+
? xStr.Equals(yStr, StringComparison.OrdinalIgnoreCase)
137+
: x.Equals(y);
138+
}
139+
140+
public int GetHashCode(object obj)
141+
{
142+
var str = obj as string;
143+
return str != null
144+
? str.ToUpper().GetHashCode()
145+
: obj.GetHashCode();
146+
}
147+
}
148+
116149
protected void SetRefSelfChildResults(FieldDefinition fieldDef, ModelDefinition refModelDef, FieldDefinition refSelf, IList childResults)
117150
{
118-
var map = new Dictionary<object, object>();
151+
var map = CreateRefMap();
119152

120153
foreach (var result in childResults)
121154
{
@@ -136,7 +169,7 @@ protected void SetRefSelfChildResults(FieldDefinition fieldDef, ModelDefinition
136169

137170
protected void SetRefFieldChildResults(FieldDefinition fieldDef, FieldDefinition refField, IList childResults)
138171
{
139-
var map = new Dictionary<object, object>();
172+
var map = CreateRefMap();
140173

141174
foreach (var result in childResults)
142175
{
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using ServiceStack.DataAnnotations;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using NUnit.Framework;
5+
using ServiceStack.Text;
6+
7+
namespace ServiceStack.OrmLite.Tests.Issues
8+
{
9+
/// <summary>This test set is used to demonstrate that the OrmLite Load methods are unexpectedly case-sensitive for
10+
/// string-based columns. A reference will not be loaded if there is a difference in case between otherwise matching parent
11+
/// value and child values. For example, parent table has RegionCode = "WEST" while it's related lookup table Region has
12+
/// RegionCode = "West".</summary>
13+
/// <remarks>Target database must be setup as case-insensitive or this exercise is pointless. Refer to custom
14+
/// openDbConnection method included in this test set.</remarks>
15+
public class CustomerWithRegion
16+
{
17+
[AutoIncrement]
18+
public int Id { get; set; }
19+
20+
public string Name { get; set; }
21+
22+
[CustomField("VARCHAR(8000) COLLATE Latin1_General_CI_AS")]
23+
public string RegionId { get; set; }
24+
25+
[Reference]
26+
public Region RegionDetail { get; set; }
27+
}
28+
29+
public class Region
30+
{
31+
[PrimaryKey]
32+
[CustomField("VARCHAR(8000) COLLATE Latin1_General_CI_AS")]
33+
public string Id { get; set; }
34+
35+
public string RegionName { get; set; }
36+
}
37+
38+
public class LoadReferencesCaseSensitiveTest : OrmLiteTestBase
39+
{
40+
const string regionCode = "West";
41+
const string regionName = "Western Region";
42+
43+
/// <summary>This test is used to demonstrate that the OrmLite Load methods are unexpectedly case-sensitive for
44+
/// string-based columns. A reference will not be loaded if there is a difference in case between otherwise matching parent
45+
/// value and child values. For example, parent table has RegionCode = "WEST" while it's related lookup table Region has
46+
/// RegionCode = "West".</summary>
47+
[Test]
48+
public void LoadReference_with_Case_Variance()
49+
{
50+
if (Dialect != Dialect.SqlServer) return;
51+
OrmLiteConfig.IsCaseInsensitive = true;
52+
53+
using (var db = OpenDbConnection())
54+
{
55+
db.DropAndCreateTable<CustomerWithRegion>();
56+
//db.GetLastSql().Print();
57+
db.DropAndCreateTable<Region>();
58+
//db.GetLastSql().Print();
59+
60+
var region = new Region { Id = regionCode, RegionName = regionName };
61+
db.Save(region);
62+
63+
var caseInsensitiveRegion = db.Single<Region>(x => x.Id == regionCode.ToUpper());
64+
Assert.That(caseInsensitiveRegion.Id, Is.EqualTo(regionCode));
65+
66+
var customers = new List<CustomerWithRegion>
67+
{
68+
new CustomerWithRegion { Name = "Acme Anvil Co.", RegionId = regionCode },
69+
new CustomerWithRegion { Name = "Penny's Poodle Emporium", RegionId = regionCode.ToUpper() },
70+
};
71+
72+
foreach (var customer in customers)
73+
{
74+
db.Save(customer);
75+
76+
var dbCustomer = db.LoadSelect<CustomerWithRegion>(s => s.Name == customer.Name).FirstOrDefault();
77+
78+
dbCustomer.PrintDump();
79+
80+
Assert.That(
81+
dbCustomer.RegionId,
82+
Is.Not.Null,
83+
string.Format("Region code missing for {0}", customer.Name));
84+
Assert.That(
85+
dbCustomer.RegionId.ToLower() == regionCode.ToLower(),
86+
string.Format("Region code incorrect for {0}", customer.Name));
87+
88+
// The following assertion will fail because LoadSelect considers CustomWithRegion.RegionCode of "WEST" != to Region.RegionCode of "West".
89+
Assert.That(
90+
dbCustomer.RegionDetail,
91+
Is.Not.Null,
92+
string.Format("Region detail record missing for {0}", customer.Name));
93+
Assert.That(
94+
dbCustomer.RegionDetail.RegionName == regionName,
95+
string.Format("Region name incorrect for {0}", customer.Name));
96+
}
97+
}
98+
99+
OrmLiteConfig.IsCaseInsensitive = false;
100+
}
101+
}
102+
}

tests/ServiceStack.OrmLite.Tests/ServiceStack.OrmLite.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
<Compile Include="Expression\SelectExpressionTests.cs" />
133133
<Compile Include="Issues\ComplexJoinWithAlias.cs" />
134134
<Compile Include="Issues\CustomFieldTests.cs" />
135+
<Compile Include="Issues\LoadReferencesCaseSensitiveTest.cs" />
135136
<Compile Include="Issues\LoadSelectIssue.cs" />
136137
<Compile Include="Issues\MultipleSelfJoinsWithNullableInts.cs" />
137138
<Compile Include="Issues\UtcDateTimeIssueTests.cs" />

0 commit comments

Comments
 (0)