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

Commit 476e980

Browse files
committed
Merge pull request #291 from CaioProiete/improve-mappings-for-legacy-databases
Add some fall-back guesses when mapping between datareader and object to better support legacy databases
2 parents bb9ce5c + 42c7066 commit 476e980

File tree

2 files changed

+116
-2
lines changed

2 files changed

+116
-2
lines changed

src/ServiceStack.OrmLite/OrmLiteWriteExtensions.cs

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
using System.Data;
1616
using System.Diagnostics;
1717
using System.Linq;
18+
using System.Text.RegularExpressions;
1819
using ServiceStack.Common.Utils;
1920
using ServiceStack.Logging;
2021

@@ -235,18 +236,27 @@ public static T PopulateWithSqlReader<T>(this T objWithProperties, IDataReader d
235236
{
236237
foreach (var fieldDef in fieldDefs)
237238
{
238-
int index = NotFound;
239+
int index;
239240
if (indexCache != null)
240241
{
241242
if (!indexCache.TryGetValue(fieldDef.Name, out index))
242243
{
243244
index = dataReader.GetColumnIndex(fieldDef.FieldName);
245+
if (index == NotFound)
246+
{
247+
index = TryGuessColumnIndex(fieldDef.FieldName, dataReader);
248+
}
249+
244250
indexCache.Add(fieldDef.Name, index);
245251
}
246252
}
247253
else
248254
{
249255
index = dataReader.GetColumnIndex(fieldDef.FieldName);
256+
if (index == NotFound)
257+
{
258+
index = TryGuessColumnIndex(fieldDef.FieldName, dataReader);
259+
}
250260
}
251261

252262
if (index == NotFound) continue;
@@ -261,6 +271,71 @@ public static T PopulateWithSqlReader<T>(this T objWithProperties, IDataReader d
261271
return objWithProperties;
262272
}
263273

274+
private static readonly Regex AllowedPropertyCharsRegex = new Regex(@"[^0-9a-zA-Z_]",
275+
RegexOptions.Compiled | RegexOptions.CultureInvariant);
276+
277+
private static int TryGuessColumnIndex(string fieldName, IDataReader dataReader)
278+
{
279+
var fieldCount = dataReader.FieldCount;
280+
for (var i = 0; i < fieldCount; i++)
281+
{
282+
var dbFieldName = dataReader.GetName(i);
283+
284+
// First guess: Maybe the DB field has underscores? (most common)
285+
// e.g. CustomerId (C#) vs customer_id (DB)
286+
var dbFieldNameWithNoUnderscores = dbFieldName.Replace("_", "");
287+
if (string.Compare(fieldName, dbFieldNameWithNoUnderscores, StringComparison.InvariantCultureIgnoreCase) == 0)
288+
{
289+
return i;
290+
}
291+
292+
// Next guess: Maybe the DB field has special characters?
293+
// e.g. Quantity (C#) vs quantity% (DB)
294+
var dbFieldNameSanitized = AllowedPropertyCharsRegex.Replace(dbFieldName, string.Empty);
295+
if (string.Compare(fieldName, dbFieldNameSanitized, StringComparison.InvariantCultureIgnoreCase) == 0)
296+
{
297+
return i;
298+
}
299+
300+
// Next guess: Maybe the DB field has special characters *and* has underscores?
301+
// e.g. Quantity (C#) vs quantity_% (DB)
302+
if (string.Compare(fieldName, dbFieldNameSanitized.Replace("_", string.Empty), StringComparison.InvariantCultureIgnoreCase) == 0)
303+
{
304+
return i;
305+
}
306+
307+
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field?
308+
// e.g. CustomerId (C#) vs t130CustomerId (DB)
309+
if (dbFieldName.EndsWith(fieldName, StringComparison.InvariantCultureIgnoreCase))
310+
{
311+
return i;
312+
}
313+
314+
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field *and* has underscores?
315+
// e.g. CustomerId (C#) vs t130_CustomerId (DB)
316+
if (dbFieldNameWithNoUnderscores.EndsWith(fieldName, StringComparison.InvariantCultureIgnoreCase))
317+
{
318+
return i;
319+
}
320+
321+
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field *and* has special characters?
322+
// e.g. CustomerId (C#) vs t130#CustomerId (DB)
323+
if (dbFieldNameSanitized.EndsWith(fieldName, StringComparison.InvariantCultureIgnoreCase))
324+
{
325+
return i;
326+
}
327+
328+
// Next guess: Maybe the DB field has some prefix that we don't have in our C# field *and* has underscores *and* has special characters?
329+
// e.g. CustomerId (C#) vs t130#Customer_I#d (DB)
330+
if (dbFieldNameSanitized.Replace("_", "").EndsWith(fieldName, StringComparison.InvariantCultureIgnoreCase))
331+
{
332+
return i;
333+
}
334+
}
335+
336+
return NotFound;
337+
}
338+
264339
internal static void Update<T>(this IDbCommand dbCmd, params T[] objs)
265340
{
266341
foreach (var obj in objs)

tests/ServiceStack.OrmLite.Tests/OrmLiteQueryTests.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,5 +218,44 @@ FROM Note
218218
Assert.That(notes[0].NoteText, Is.EqualTo("Hello world 5"));
219219
}
220220
}
221-
}
221+
222+
class CustomerDto
223+
{
224+
public int CustomerId { get; set; }
225+
public string @CustomerName { get; set; }
226+
public DateTime Customer_Birth_Date { get; set; }
227+
}
228+
229+
[Test]
230+
[TestCase("customer_id", "customer_name", "customer_birth_date")]
231+
[TestCase("customerid%", "@customername", "customer_b^irth_date")]
232+
[TestCase("customerid_%", "@customer_name", "customer$_birth_#date")]
233+
[TestCase("c!u@s#t$o%m^e&r*i(d_%", "__cus_tomer__nam_e__", "~cus`tomer$_birth_#date")]
234+
[TestCase("t030CustomerId", "t030CustomerName", "t030Customer_birth_date")]
235+
[TestCase("t030_customer_id", "t030_customer_name", "t130_customer_birth_date")]
236+
[TestCase("t030#Customer_I#d", "t030CustomerNa$^me", "t030Cust^omer_birth_date")]
237+
public void Can_query_CustomerDto_and_map_db_fields_not_identical_by_guessing_the_mapping(string field1Name, string field2Name, string field3Name)
238+
{
239+
using (var db = ConnectionString.OpenDbConnection())
240+
{
241+
var sql = string.Format(@"
242+
SELECT 1 AS [{0}], 'John' AS [{1}], '1970-01-01' AS [{2}]
243+
UNION ALL
244+
SELECT 2 AS [{0}], 'Jane' AS [{1}], '1980-01-01' AS [{2}]",
245+
field1Name, field2Name, field3Name);
246+
247+
var customers = db.Query<CustomerDto>(sql);
248+
249+
Assert.That(customers.Count, Is.EqualTo(2));
250+
251+
Assert.That(customers[0].CustomerId, Is.EqualTo(1));
252+
Assert.That(customers[0].CustomerName, Is.EqualTo("John"));
253+
Assert.That(customers[0].Customer_Birth_Date, Is.EqualTo(new DateTime(1970, 01, 01)));
254+
255+
Assert.That(customers[1].CustomerId, Is.EqualTo(2));
256+
Assert.That(customers[1].CustomerName, Is.EqualTo("Jane"));
257+
Assert.That(customers[1].Customer_Birth_Date, Is.EqualTo(new DateTime(1980, 01, 01)));
258+
}
259+
}
260+
}
222261
}

0 commit comments

Comments
 (0)