Skip to content

Commit 47ef38d

Browse files
committed
Update SqlDataReaderExtensions.cs
In #dde0ade, we replaced `reader.GetValue()` with strongly typed extension methods, such as `reader.GetInteger()`. Under the hood, these simply looked up the ordinal value of the column name, and then called the underlying `SqlDataReader` methods such as `GetInt32()`, `GetBoolean()`, &c. Unfortunately, that approach doesn't work because the values being returned aren't actually e.g. `INT`, `BIT`, &c. Instead, they're (almost always) strings, especially when they map to `AttributeKey` or `AttributeValue` (as is true of attributes, including `Key`, `ParentId`, `ContentType`, `IsHidden`, &c.). As such, instead of calling the underlying strongly typed methods, we need to call `SqlDataReader.GetValue()` and then do a conversion. To support this, the private `GetValue()` extension is now setup to instead accept a `TryParse` delegate so that e.g. `Boolean.TryParse` can be passed as the delegate. This isn't all that elegant, but it should work, and it still results in much cleaner code in `SqlTopicRepository`, which was the original objective.
1 parent 28948f7 commit 47ef38d

1 file changed

Lines changed: 18 additions & 8 deletions

File tree

OnTopic.Data.Sql/SqlDataReaderExtensions.cs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ namespace OnTopic.Data.Sql {
1919
/// </summary>
2020
internal static class SqlDataReaderExtensions {
2121

22+
23+
/*==========================================================================================================================
24+
| DELEGATE: TRY PARSE
25+
\-------------------------------------------------------------------------------------------------------------------------*/
26+
public delegate bool TryParse<in String, U, out Boolean>(String input, out U output);
27+
2228
/*==========================================================================================================================
2329
| METHOD: GET INTEGER
2430
\-------------------------------------------------------------------------------------------------------------------------*/
@@ -28,7 +34,7 @@ internal static class SqlDataReaderExtensions {
2834
/// <param name="reader">The <see cref="SqlDataReader"/> object.</param>
2935
/// <param name="columnName">The name of the column to retrieve the value from.</param>
3036
internal static int GetInteger(this SqlDataReader reader, string columnName) =>
31-
GetValue<int>(reader, columnName, reader.GetInt32, -1);
37+
GetValue<int>(reader, columnName, Int32.TryParse, -1);
3238

3339
/*==========================================================================================================================
3440
| METHOD: GET STRING
@@ -39,7 +45,7 @@ internal static int GetInteger(this SqlDataReader reader, string columnName) =>
3945
/// <param name="reader">The <see cref="SqlDataReader"/> object.</param>
4046
/// <param name="columnName">The name of the column to retrieve the value from.</param>
4147
internal static string GetString(this SqlDataReader reader, string columnName) =>
42-
GetValue<string>(reader, columnName, reader.GetString, String.Empty);
48+
GetValue<string>(reader, columnName, (string source, out string value) => { value = source; return true; }, String.Empty);
4349

4450
/*==========================================================================================================================
4551
| METHOD: GET DATE/TIME
@@ -50,7 +56,7 @@ internal static string GetString(this SqlDataReader reader, string columnName) =
5056
/// <param name="reader">The <see cref="SqlDataReader"/> object.</param>
5157
/// <param name="columnName">The name of the column to retrieve the value from.</param>
5258
internal static DateTime GetDateTime(this SqlDataReader reader, string columnName) =>
53-
GetValue<DateTime>(reader, columnName, reader.GetDateTime, DateTime.MinValue);
59+
GetValue<DateTime>(reader, columnName, DateTime.TryParse, DateTime.MinValue);
5460

5561
/*==========================================================================================================================
5662
| METHOD: GET BOOLEAN
@@ -61,7 +67,7 @@ internal static DateTime GetDateTime(this SqlDataReader reader, string columnNam
6167
/// <param name="reader">The <see cref="SqlDataReader"/> object.</param>
6268
/// <param name="columnName">The name of the column to retrieve the value from.</param>
6369
internal static bool GetBoolean(this SqlDataReader reader, string columnName) =>
64-
GetValue<bool>(reader, columnName, reader.GetBoolean, false);
70+
GetValue<bool>(reader, columnName, Boolean.TryParse, false);
6571

6672
/*==========================================================================================================================
6773
| METHOD: GET VALUE
@@ -71,18 +77,22 @@ internal static bool GetBoolean(this SqlDataReader reader, string columnName) =>
7177
/// </summary>
7278
/// <param name="reader">The <see cref="SqlDataReader"/> object.</param>
7379
/// <param name="columnName">The name of the column to retrieve the value from.</param>
74-
/// <param name="getter">Function to call in order to retrieve the strongly typed value from the reader.</param>
80+
/// <param name="parser">Function to call in order to convert the string value to a strongly typed value.</param>
7581
/// <param name="defaultValue">The default value, in case <see cref="DBNull"/> is returned.</param>
7682
private static T GetValue<T>(
7783
SqlDataReader reader,
7884
string columnName,
79-
Func<int, T> getter,
85+
TryParse<string, T, bool> parser,
8086
object defaultValue
8187
) {
8288
Contract.Requires(columnName, nameof(columnName));
8389
var ordinal = reader.GetOrdinal(columnName);
84-
if (!reader.IsDBNull(ordinal)) {
85-
return getter(ordinal);
90+
if (reader.IsDBNull(ordinal)) {
91+
return (T)defaultValue;
92+
}
93+
var value = reader.GetValue(ordinal).ToString();
94+
if (parser(value, out var output)) {
95+
return output;
8696
}
8797
return (T)defaultValue;
8898
}

0 commit comments

Comments
 (0)