Skip to content

Commit

Permalink
Fixed #16 - ConvertNumericAttribute can now convert numeric types to …
Browse files Browse the repository at this point in the history
…booleans.
  • Loading branch information
abe545 committed Mar 17, 2015
1 parent 4c13b7d commit 8bc9404
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 2 deletions.
27 changes: 26 additions & 1 deletion CodeOnlyStoredProcedure/RowFactory/AccessorFactoryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,14 @@ protected static Expression CreateUnboxedRetrieval<T>(
if (expectedDbType != null)
{
res = Expression.Call(dbReader, typeof(IDataRecord).GetMethod("Get" + Type.GetTypeCode(expectedDbType)), index);
res = Expression.Convert(res, dbType);
if (type == typeof(bool) || type == typeof(bool?))
{
res = Expression.NotEqual(res, Zero(expectedDbType));
if (type == typeof(bool?))
res = Expression.Convert(res, type);
}
else
res = Expression.Convert(res, dbType);
}
else
res = Expression.Call(dbReader, typeof(IDataRecord).GetMethod("Get" + Type.GetTypeCode(dbType)), index);
Expand Down Expand Up @@ -324,5 +331,23 @@ protected static Expression CreateUnboxedRetrieval<T>(

return Expression.Block(type, parms, body);
}

protected static Expression Zero(Type dbType, bool isNullable = false)
{
if (dbType == typeof(short))
return Expression.Constant((short)0, isNullable ? typeof(short?) : dbType);
if (dbType == typeof(byte))
return Expression.Constant((byte)0, isNullable ? typeof(byte?) : dbType);
if (dbType == typeof(double))
return Expression.Constant(0.0, isNullable ? typeof(double?) : dbType);
if (dbType == typeof(float))
return Expression.Constant(0f, isNullable ? typeof(float?) : dbType);
if (dbType == typeof(decimal))
return Expression.Constant(0M, isNullable ? typeof(decimal?) : dbType);
if (dbType == typeof(long))
return Expression.Constant(0L, isNullable ? typeof(long?) : dbType);

return Expression.Constant(0, isNullable ? typeof(int?) : dbType);
}
}
}
18 changes: 17 additions & 1 deletion CodeOnlyStoredProcedure/RowFactory/ValueAccessorFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,23 @@ public override Expression CreateExpressionToGetValueFromReader(IDataReader read
if (dbColumnType != expectedType)
{
if (convertNumeric)
res = Expression.Convert(res, type);
{
if (expectedType == typeof(bool))
{
if (isNullable)
{
// res = res == null ? null : (bool?)(bool)res
res = Expression.Condition(
Expression.Equal(res, Expression.Constant(null)),
Expression.Constant(default(bool?), typeof(bool?)),
Expression.Convert(Expression.NotEqual(res, Zero(dbColumnType, true)), typeof(bool?)));
}
else
res = Expression.NotEqual(res, Zero(dbColumnType));
}
else
res = Expression.Convert(res, type);
}
else
throw new StoredProcedureColumnException(type, dbColumnType, propertyName);
}
Expand Down
200 changes: 200 additions & 0 deletions CodeOnlyTests/RowFactory/ComplexTypeRowFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,188 @@ public void EnumPropertiesWillConvertNumericTypes()
.And.Match<EnumValueTypesConverted>(i => i.FooBar == FooBar.Foo, "the transformer doubles the result from the database");
}

[TestMethod]
public void NumericTypeWillConvertToTrueBoolIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 1 }
}, false);

var toTest = RowFactory<ConvertToBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToBool>(i => i.IsEnabled, "the property should be converted to bool");
}

[TestMethod]
public void NumericTypeWillConvertToFalseBoolIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 0 }
}, false);

var toTest = RowFactory<ConvertToBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToBool>(i => !i.IsEnabled, "the property should be converted to bool");
}

[TestMethod]
public void NumericTypeWillConvertToTrueBoolIfMarkedWithConvert_WhenTransformerPassed()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 1 }
}, true);

var toTest = RowFactory<ConvertToBool>.Create().ParseRows(
reader, new[] { Mock.Of<IDataTransformer>() }, CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToBool>(i => i.IsEnabled, "the property should be converted to bool");
}

[TestMethod]
public void NumericTypeWillConvertToFalseBoolIfMarkedWithConvert_WhenTransformerPassed()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 0L }
}, true);

var toTest = RowFactory<ConvertToBool>.Create().ParseRows(
reader, new[] { Mock.Of<IDataTransformer>() }, CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToBool>(i => !i.IsEnabled, "the property should be converted to bool");
}

[TestMethod]
public void NumericTypeWillConvertToTrueNullableBoolIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 1M }
}, false);

var toTest = RowFactory<ConvertToNullableBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToNullableBool>(i => i.IsEnabled.Value, "the property should be converted to bool");
}

[TestMethod]
public void NullNumericTypeWillConvertToNullNullableBoolIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", null }
}, true);
Mock.Get(reader).Setup(rdr => rdr.GetFieldType(0)).Returns(typeof(int));

var toTest = RowFactory<ConvertToNullableBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToNullableBool>(i => !i.IsEnabled.HasValue, "the property should not have a value");
}

[TestMethod]
public void NumericTypeWillConvertToFalseNullableBoolIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 0 }
}, false);

var toTest = RowFactory<ConvertToNullableBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToNullableBool>(i => !i.IsEnabled.Value, "the property should be converted to bool");
}

[TestMethod]
public void NumericTypeWillConvertToTrueNullableBoolIfMarkedWithConvert_WhenTransformerPassed()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 1.0 }
}, true);

var toTest = RowFactory<ConvertToNullableBool>.Create().ParseRows(
reader, new[] { Mock.Of<IDataTransformer>() }, CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToNullableBool>(i => i.IsEnabled.Value, "the property should be converted to bool");
}

[TestMethod]
public void NumericTypeWillConvertToFalseNullableBoolIfMarkedWithConvert_WhenTransformerPassed()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", 0 }
}, true);

var toTest = RowFactory<ConvertToNullableBool>.Create().ParseRows(
reader, new[] { Mock.Of<IDataTransformer>() }, CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToNullableBool>(i => !i.IsEnabled.Value, "the property should be converted to bool");
}

[TestMethod]
public void NullNumericTypeWillConvertToNullNullableBoolIfMarkedWithConvert_WhenTransformerPassed()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", null }
}, true);
Mock.Get(reader).Setup(rdr => rdr.GetFieldType(0)).Returns(typeof(short));

var toTest = RowFactory<ConvertToNullableBool>.Create().ParseRows(
reader, new[] { Mock.Of<IDataTransformer>() }, CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertToNullableBool>(i => !i.IsEnabled.HasValue, "the property should not have a value");
}

[TestMethod]
public void TrueBoolWillConvertToNumericTypeIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", true }
}, false);

var toTest = RowFactory<ConvertFromBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertFromBool>(i => i.IsEnabled == 1, "the property should be converted from bool");
}

[TestMethod]
public void FalseBoolWillConvertToNumericTypeIfMarkedWithConvert()
{
var reader = CreateDataReader(new Dictionary<string, object>
{
{ "IsEnabled", false }
}, false);

var toTest = RowFactory<ConvertFromBool>.Create().ParseRows(
reader, Enumerable.Empty<IDataTransformer>(), CancellationToken.None);

toTest.Should().ContainSingle("because one row is returned").Which
.Should().Match<ConvertFromBool>(i => i.IsEnabled == 0, "the property should be converted from bool");
}

private static IDataReader CreateDataReader(Dictionary<string, object> values, bool setupGetValue = true)
{
var keys = values.Keys.OrderBy(s => s).ToList();
Expand Down Expand Up @@ -821,6 +1003,24 @@ private class EnumValueTypesConverted
public FooBar FooBar { get; set; }
}

private class ConvertToBool
{
[ConvertNumeric]
public bool IsEnabled { get; set; }
}

private class ConvertToNullableBool
{
[ConvertNumeric]
public bool? IsEnabled { get; set; }
}

private class ConvertFromBool
{
[ConvertNumeric]
public int IsEnabled { get; set; }
}

private class StaticValueAttribute : DataTransformerAttributeBase
{
public object Result { get; set; }
Expand Down

0 comments on commit 8bc9404

Please sign in to comment.