Skip to content
This repository was archived by the owner on Apr 17, 2019. It is now read-only.

Commit 583a764

Browse files
Burgynsatano
authored andcommitted
Support for materialize float and DateTimeOffset types. (#149)
* Support for materialize float and DateTimeOffset types. * CodeFactor issues * Code review issues.
1 parent e7e801b commit 583a764

File tree

5 files changed

+120
-38
lines changed

5 files changed

+120
-38
lines changed

Kros.KORM/src/Kros.KORM/Materializer/DynamicMethodModelFactory.cs

Lines changed: 73 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Kros.Caching;
2+
using Kros.Extensions;
23
using Kros.KORM.Converter;
34
using Kros.KORM.Injection;
45
using Kros.KORM.Metadata;
@@ -27,7 +28,6 @@ public class DynamicMethodModelFactory : IModelFactory
2728
private readonly static List<IConverter> _converters = new List<IConverter>();
2829
private readonly static List<IInjector> _injectors = new List<IInjector>();
2930

30-
private readonly static ICache<string, MethodInfo> _methodsInfo = new Cache<string, MethodInfo>();
3131
private readonly static MethodInfo _fnIsDBNull = typeof(IDataRecord).GetMethod("IsDBNull");
3232
private readonly static MethodInfo _fnGetValue = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
3333
private readonly static MethodInfo _fnConvert = typeof(IConverter).GetMethod("Convert");
@@ -39,6 +39,9 @@ public class DynamicMethodModelFactory : IModelFactory
3939
private readonly static MethodInfo _fnInjectorsListGetItem = typeof(List<IInjector>).GetProperty("Item").GetGetMethod();
4040
private readonly static MethodInfo _fnInjectorMethodInfo =
4141
typeof(IInjector).GetMethod(nameof(IInjector.GetValue), new Type[] { typeof(string) });
42+
private readonly static Dictionary<string, MethodInfo> _readerValueGetters = InitReaderValueGetters();
43+
private readonly static MethodInfo _getValueMethodInfo =
44+
typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
4245

4346
#endregion
4447

@@ -51,9 +54,7 @@ public class DynamicMethodModelFactory : IModelFactory
5154
/// <exception cref="System.ArgumentNullException">databaseMapper;Argument 'databaseMapper' is required.</exception>
5255
public DynamicMethodModelFactory(IDatabaseMapper databaseMapper)
5356
{
54-
Check.NotNull(databaseMapper, nameof(databaseMapper));
55-
56-
_databaseMapper = databaseMapper;
57+
_databaseMapper = Check.NotNull(databaseMapper, nameof(databaseMapper));
5758
}
5859

5960
#endregion
@@ -111,8 +112,7 @@ private static T FactoryForValueType<T>(IDataReader reader)
111112
Type destType = typeof(T);
112113
Type srcType = reader.GetFieldType(0);
113114

114-
var valueGetter = _methodsInfo.Get(srcType.Name,
115-
() => typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) }));
115+
var valueGetter = GetReaderValueGetter(srcType);
116116

117117
var value = valueGetter.Invoke(reader, new object[] { 0 });
118118
if (destType.Name == srcType.Name)
@@ -261,10 +261,9 @@ private static void FillingValuesWithoutConverter(ILGenerator ilGenerator,
261261
ColumnInfo columnInfo,
262262
Type srcType)
263263
{
264-
bool? castNeeded = null;
264+
bool castNeeded = false;
265265

266-
var valuegetter = _methodsInfo.Get(srcType.Name,
267-
() => typeof(IDataRecord).GetMethod("Get" + srcType.Name, new Type[] { typeof(int) }));
266+
MethodInfo valuegetter = GetReaderValueGetter(srcType);
268267

269268
if (valuegetter != null
270269
&& valuegetter.ReturnType == srcType
@@ -273,37 +272,80 @@ private static void FillingValuesWithoutConverter(ILGenerator ilGenerator,
273272
{
274273
castNeeded = false;
275274
}
275+
else if ((srcType == columnInfo.PropertyInfo.PropertyType) ||
276+
(srcType == Nullable.GetUnderlyingType(columnInfo.PropertyInfo.PropertyType)))
277+
{
278+
valuegetter = _getValueMethodInfo;
279+
castNeeded = true;
280+
}
276281
else
277282
{
278-
if (srcType == typeof(byte[]) && srcType == columnInfo.PropertyInfo.PropertyType)
279-
{
280-
valuegetter = typeof(IDataRecord).GetMethod("GetValue", new Type[] { typeof(int) });
281-
castNeeded = true;
282-
}
283+
throw new InvalidOperationException(
284+
Properties.Resources.CannotMaterializeSourceValue.Format(srcType, columnInfo.PropertyInfo.PropertyType));
283285
}
284286

285-
if (castNeeded.HasValue)
286-
{
287-
ilGenerator.Emit(OpCodes.Ldarg_0);
288-
ilGenerator.Emit(OpCodes.Ldc_I4, fieldIndex);
289-
ilGenerator.Emit(OpCodes.Callvirt, valuegetter);
287+
ilGenerator.Emit(OpCodes.Ldarg_0);
288+
ilGenerator.Emit(OpCodes.Ldc_I4, fieldIndex);
289+
ilGenerator.Emit(OpCodes.Callvirt, valuegetter);
290290

291-
if (castNeeded.Value)
292-
{
293-
ilGenerator.Emit(OpCodes.Castclass, columnInfo.PropertyInfo.PropertyType);
294-
}
295-
else
291+
if (castNeeded)
292+
{
293+
EmitCastValue(ilGenerator, columnInfo, srcType);
294+
}
295+
else
296+
{
297+
if (Nullable.GetUnderlyingType(columnInfo.PropertyInfo.PropertyType) != null)
296298
{
297-
if (Nullable.GetUnderlyingType(columnInfo.PropertyInfo.PropertyType) != null)
298-
{
299-
ilGenerator.Emit(OpCodes.Newobj,
300-
columnInfo.PropertyInfo.PropertyType.GetConstructor(
301-
new Type[] { Nullable.GetUnderlyingType(columnInfo.PropertyInfo.PropertyType) }));
302-
}
299+
ilGenerator.Emit(OpCodes.Newobj,
300+
columnInfo.PropertyInfo.PropertyType.GetConstructor(
301+
new Type[] { Nullable.GetUnderlyingType(columnInfo.PropertyInfo.PropertyType) }));
303302
}
303+
}
304304

305-
ilGenerator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true));
305+
ilGenerator.Emit(OpCodes.Callvirt, columnInfo.PropertyInfo.GetSetMethod(true));
306+
}
307+
308+
private static void EmitCastValue(ILGenerator ilGenerator, ColumnInfo columnInfo, Type srcType)
309+
{
310+
if (srcType.IsValueType)
311+
{
312+
ilGenerator.Emit(OpCodes.Unbox_Any, columnInfo.PropertyInfo.PropertyType);
313+
}
314+
else
315+
{
316+
ilGenerator.Emit(OpCodes.Castclass, columnInfo.PropertyInfo.PropertyType);
306317
}
307318
}
319+
320+
private static Dictionary<string, MethodInfo> InitReaderValueGetters()
321+
{
322+
var getters = new Dictionary<string, MethodInfo>(StringComparer.CurrentCultureIgnoreCase);
323+
324+
MethodInfo CreateReaderValueGetter(string typeName)
325+
=> typeof(IDataRecord).GetMethod($"Get{typeName}", new Type[] { typeof(int) });
326+
327+
void Add<T>()
328+
=> getters.Add(typeof(T).Name, CreateReaderValueGetter(typeof(T).Name));
329+
330+
Add<Boolean>();
331+
Add<Byte>();
332+
Add<Char>();
333+
Add<DateTime>();
334+
Add<Decimal>();
335+
Add<Double>();
336+
Add<Guid>();
337+
Add<Int16>();
338+
Add<Int32>();
339+
Add<Int64>();
340+
341+
Add<String>();
342+
343+
getters.Add(nameof(Single), CreateReaderValueGetter("Float"));
344+
345+
return getters;
346+
}
347+
348+
private static MethodInfo GetReaderValueGetter(Type srcType)
349+
=> _readerValueGetters.ContainsKey(srcType.Name) ? _readerValueGetters[srcType.Name] : null;
308350
}
309351
}

Kros.KORM/src/Kros.KORM/Properties/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Kros.KORM/src/Kros.KORM/Properties/Resources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@
120120
<data name="BinaryOperatorIsNotSupported" xml:space="preserve">
121121
<value>The binary operator '{0}' is not supported.</value>
122122
</data>
123+
<data name="CannotMaterializeSourceValue" xml:space="preserve">
124+
<value>Can't materialize source value type '{0}' to destination type '{1}' without defined converter.</value>
125+
</data>
123126
<data name="CannotOpenConnectionWhenGeneratingPrimaryKeys" xml:space="preserve">
124127
<value>There was a failure to open the database connection at the time the primary keys are generated. Try add 'Persist Security Info=TRUE' to the connection string.</value>
125128
</data>

Kros.KORM/tests/Kros.KORM.UnitTests/Integration/DbSetShould.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ private class DataTypesData
3434
public byte ColByte { get; set; }
3535
public int ColInt32 { get; set; }
3636
public long ColInt64 { get; set; }
37-
//public float ColSingle { get; set; }
37+
public float ColSingle { get; set; }
3838
public double ColDouble { get; set; }
3939
public decimal ColDecimal { get; set; }
4040
public decimal ColCurrency { get; set; }
4141
public DateTime ColDate { get; set; }
4242
public DateTime ColDateTime { get; set; }
4343
public DateTime ColDateTime2 { get; set; }
44-
//public DateTimeOffset ColDateTimeOffset { get; set; }
44+
public DateTimeOffset ColDateTimeOffset { get; set; }
4545
public DateTime ColSmallDateTime { get; set; }
4646
public Guid ColGuid { get; set; }
4747
public bool ColBool { get; set; }
@@ -773,14 +773,14 @@ private static DataTypesData CreateRecord(int id)
773773
ColByte = (byte)id,
774774
ColInt32 = id * 100,
775775
ColInt64 = id * 100000000000,
776-
//ColSingle = id * (float)100000000000.12345,
776+
ColSingle = id * (float)100000000000.12345,
777777
ColDouble = id * 100000000000.12345,
778778
ColDecimal = id * (decimal)100000000000.12345,
779779
ColCurrency = id * (decimal)100000000000.12345,
780780
ColDate = new DateTime(1978, 12, id),
781781
ColDateTime = new DateTime(1978, 12, id, 10, 11, 22),
782782
ColDateTime2 = new DateTime(1978, 12, id, 10, 11, 22),
783-
//ColDateTimeOffset = new DateTimeOffset(1978, 12, id, 10, 11, 22, 123, TimeSpan.FromHours(id)),
783+
ColDateTimeOffset = new DateTimeOffset(1978, 12, id, 10, 11, 22, 123, TimeSpan.FromHours(id)),
784784
ColSmallDateTime = new DateTime(1978, 12, id, 10, 11, 0),
785785
ColGuid = Guid.Parse($"{id}0000000-0000-0000-0000-000000000000"),
786786
ColBool = (id % 2) == 0,

Kros.KORM/tests/Kros.KORM.UnitTests/Materializer/MethodModelFactoryShould.cs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public void CreateFactoryWhichKnowFillingObjectsWithPrimitiveTypes()
3535
foo.PropertyDouble.Should().Be(45.78);
3636
foo.PropertyDecimal.Should().Be(785.78M);
3737
foo.PropertyDateTime.Should().Be(new DateTime(1980, 7, 24));
38+
foo.DateTimeOffset.Should().Be(new DateTimeOffset(1985, 9, 20, 10, 11, 22, 123, TimeSpan.FromHours(5)));
39+
foo.DateTimeOffsetNullable.Should().Be(new DateTimeOffset(1985, 9, 20, 10, 11, 22, 123, TimeSpan.FromHours(5)));
40+
foo.Float.Should().Be(12.8F);
41+
foo.FloatNullable.Should().Be(45.89F);
3842
foo.Is.Should().BeTrue();
3943
foo.PropertyGuid.Should().Be(new Guid("ddc995d7-4dda-41ca-abab-7f45e651784a"));
4044
}
@@ -350,6 +354,10 @@ private TableInfo CreateTableInfo()
350354
new ColumnInfo(){ Name = "PropertyDateTimeNullable", PropertyInfo = GetPropertyInfo<Foo>("PropertyDateTimeNullable")},
351355
new ColumnInfo(){ Name = "PropertyEnumConv", PropertyInfo = GetPropertyInfo<Foo>("PropertyEnumConv"), Converter = new TestEnumConverter()},
352356
new ColumnInfo(){ Name = "Age", PropertyInfo = GetPropertyInfo<Foo>("Age")},
357+
new ColumnInfo(){ Name = "Float", PropertyInfo = GetPropertyInfo<Foo>("Float")},
358+
new ColumnInfo(){ Name = "FloatNullable", PropertyInfo = GetPropertyInfo<Foo>("FloatNullable")},
359+
new ColumnInfo(){ Name = "DateTimeOffset", PropertyInfo = GetPropertyInfo<Foo>("DateTimeOffset")},
360+
new ColumnInfo(){ Name = "DateTimeOffsetNullable", PropertyInfo = GetPropertyInfo<Foo>("DateTimeOffsetNullable")},
353361
new ColumnInfo(){ Name = "Service", PropertyInfo = GetPropertyInfo<Foo>("Service")}
354362
};
355363

@@ -367,7 +375,11 @@ private List<Dictionary<string, object>> CreateData()
367375
List<Dictionary<string, object>> ret = new List<Dictionary<string, object>>();
368376

369377
AddRow(ret, 1, "Hello", 45.78, (decimal)785.78, new DateTime(1980, 7, 24),
370-
true, new Guid("ddc995d7-4dda-41ca-abab-7f45e651784a"), 18.5F);
378+
true, new Guid("ddc995d7-4dda-41ca-abab-7f45e651784a"), 18.5F,
379+
floatValue: 12.8F,
380+
floatNullableValue: 45.89F,
381+
dateTimeOffset: new DateTimeOffset(1985, 9, 20, 10, 11, 22, 123, TimeSpan.FromHours(5)),
382+
dateTimeOffsetNullable: new DateTimeOffset(1985, 9, 20, 10, 11, 22, 123, TimeSpan.FromHours(5)));
371383

372384
return ret;
373385
}
@@ -380,7 +392,11 @@ private static void AddRow(List<Dictionary<string, object>> ret,
380392
DateTime birthday,
381393
bool iS,
382394
Guid guid,
383-
Single? age)
395+
Single? age,
396+
float floatValue,
397+
float? floatNullableValue,
398+
DateTimeOffset dateTimeOffset,
399+
DateTimeOffset? dateTimeOffsetNullable)
384400
{
385401
Dictionary<string, object> row = new Dictionary<string, object>() { { "Id", id },
386402
{ "FirstName", firstName },
@@ -389,7 +405,11 @@ private static void AddRow(List<Dictionary<string, object>> ret,
389405
{ "Birthday", birthday},
390406
{ "Is", iS},
391407
{ "PropertyGuid", guid},
392-
{ "Age", age} };
408+
{ "Age", age},
409+
{ "Float", floatValue},
410+
{ "FloatNullable", floatNullableValue},
411+
{ "DateTimeOffset", dateTimeOffset},
412+
{ "DateTimeOffsetNullable", dateTimeOffsetNullable}};
393413

394414
ret.Add(row);
395415
}
@@ -449,6 +469,14 @@ private class Foo
449469

450470
public double? Age { get; set; }
451471

472+
public float Float { get; set; }
473+
474+
public float? FloatNullable { get; set; }
475+
476+
public DateTimeOffset DateTimeOffset { get; set; }
477+
478+
public DateTimeOffset? DateTimeOffsetNullable { get; set; }
479+
452480
[NoMap()]
453481
public TestService Service { get; set; }
454482
}

0 commit comments

Comments
 (0)