Skip to content

Commit

Permalink
Merge pull request #95 from abe545/add-binary-blob-support
Browse files Browse the repository at this point in the history
Add binary blob support. Fixes #38.
  • Loading branch information
abe545 authored Jan 1, 2017
2 parents eed4800 + 4e14a00 commit aa3ae4b
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 7 deletions.
19 changes: 15 additions & 4 deletions CodeOnlyStoredProcedure/TypeExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public static class TypeExtensions
typeof(DateTimeOffset),
typeof(TimeSpan),
typeof(Char),
typeof(Guid)
typeof(Guid),
typeof(byte[])
};

internal static IEnumerable<PropertyInfo> GetMappedProperties(
Expand All @@ -62,7 +63,7 @@ internal static bool IsEnumeratedType(this Type t)
{
Contract.Requires(t != null);

return t.IsArray || typeof(IEnumerable).IsAssignableFrom(t) && t.IsGenericType;
return t != typeof(byte[]) && (t.IsArray || typeof(IEnumerable).IsAssignableFrom(t) && t.IsGenericType);
}

internal static Type GetEnumeratedType(this Type t)
Expand Down Expand Up @@ -131,6 +132,8 @@ internal static DbType InferDbType(this Type type)
return DbType.SByte;
else if (type == typeof(Char))
return DbType.StringFixedLength;
else if (type == typeof(byte[]))
return DbType.Binary;

return DbType.Object;
}
Expand Down Expand Up @@ -167,6 +170,8 @@ internal static DbType InferDbType(this Type type)
return SqlDbType.NChar;
else if (type == typeof(Single))
return SqlDbType.Real;
else if (type == typeof(byte[]))
return SqlDbType.VarBinary;

return null;
}
Expand Down Expand Up @@ -199,10 +204,14 @@ internal static void SetTypePrecisionAndScale(this Type type,
case DbType.StringFixedLength:
case DbType.AnsiString:
case DbType.String:
case DbType.Binary:
parameter.Size = specifiedSize ?? defaultSize;
break;

case DbType.Binary:
if (specifiedSize.HasValue)
parameter.Size = specifiedSize.Value;
break;

case DbType.Currency:
case DbType.Decimal:
parameter.Precision = specifiedPrecision ?? defaultPrecision;
Expand Down Expand Up @@ -242,9 +251,11 @@ internal static SqlMetaData CreateSqlMetaData(this Type type,
case SqlDbType.NVarChar:
case SqlDbType.Text:
case SqlDbType.NText:
case SqlDbType.VarBinary:
return new SqlMetaData(columnName, specifiedSqlDbType.Value, specifiedSize ?? defaultSize);

case SqlDbType.VarBinary:
return new SqlMetaData(columnName, specifiedSqlDbType.Value, specifiedSize ?? 8000);

case SqlDbType.Decimal:
return new SqlMetaData(columnName, specifiedSqlDbType.Value, specifiedPrecision ?? defaultPrecision, specifiedScale ?? defaultScale);

Expand Down
5 changes: 4 additions & 1 deletion CodeOnlyStoredProcedures.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>A library for easily calling Stored Procedures in .NET. Works great with Entity Framework Code First models.
Code Only Stored Procedures will not create any Stored Procedures on your database. Instead, its aim is to make it easy to call your existing stored procedures by writing simple code.</description>
<releaseNotes>2.3.0
<releaseNotes>2.4.0
Added support for binary blobs for both results and parameters by using a byte array.

2.3.0
Can now opt in to not clone the database connection before executing a StoredProcedure.
Can now execute a non-query using the dynamic syntax.
Fixed bug where hierarchical result sets could not be marked as optional.
Expand Down
27 changes: 27 additions & 0 deletions CodeOnlyTests/TypeExtensionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,33 @@ public void InferDbType_ReturnsDateTimeOffset()
{
typeof(DateTimeOffset).InferDbType().Should().Be(DbType.DateTimeOffset);
}

[TestMethod]
public void InferDbType_ReturnsBinary_ForByteArray()
{
typeof(byte[]).InferDbType().Should().Be(DbType.Binary);
}
#endregion

#region TryInferSqlDbType
[TestMethod]
public void TryInferSqlDbType_ReturnsNVarChar_ForEnum()
{
var result = typeof(EnumToTest).TryInferSqlDbType();
result.Should().Be(SqlDbType.NVarChar, "because an enum should be passed as a varchar");
}

[TestMethod]
public void TryInferSqlDbType_ReturnsDateTimeOffset()
{
typeof(DateTimeOffset).TryInferSqlDbType().Should().Be(SqlDbType.DateTimeOffset);
}

[TestMethod]
public void TryInferSqlDbType_ReturnsBinary_ForByteArray()
{
typeof(byte[]).TryInferSqlDbType().Should().Be(SqlDbType.VarBinary);
}
#endregion

#region CreateSqlMetaData
Expand Down
62 changes: 62 additions & 0 deletions SmokeTests/DynamicSyntax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -689,5 +689,67 @@ async Task<Tuple<bool, string>> NonExistant_WithoutResults_Await_Asynchronously(
}
}
#endregion

#region Binary
[SmokeTest("Dynamic Syntax Binary ResultSet")]
Tuple<bool, string> ExecuteBinarySync(IDbConnection db)
{
byte[] res = db.Execute(Program.timeout).usp_GetIntAsBytes(@toBytes: 42);
if (BitConverter.IsLittleEndian)
Array.Reverse(res);

if (BitConverter.ToInt32(res, 0) != 42)
return Tuple.Create(false, "The bytes returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");
}

[SmokeTest("Dynamic Syntax Binary ResultSet (await)")]
async Task<Tuple<bool, string>> ExecuteBinaryAsync(IDbConnection db)
{
byte[] res = await db.ExecuteAsync(Program.timeout).usp_GetIntAsBytes(@toBytes: 42);
if (BitConverter.IsLittleEndian)
Array.Reverse(res);

if (BitConverter.ToInt32(res, 0) != 42)
return Tuple.Create(false, "The bytes returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");
}

[SmokeTest("Dynamic Syntax Binary ResultSet (task)")]
Task<Tuple<bool, string>> ExecuteBinaryTask(IDbConnection db)
{
Task<byte[]> t = db.ExecuteAsync(Program.timeout).usp_GetIntAsBytes(@toBytes: 42);

return t.ContinueWith(r =>
{
var res = r.Result;

if (BitConverter.IsLittleEndian)
Array.Reverse(res);

if (BitConverter.ToInt32(res, 0) != 42)
return Tuple.Create(false, "The bytes returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");

});
}

[SmokeTest("Dynamic Syntax Binary Parameter")]
Tuple<bool, string> ExecuteBinaryParameter(IDbConnection db)
{
byte[] bytes = BitConverter.GetBytes(123456);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);

int res = db.Execute(Program.timeout).usp_GetBytesAsInt(@toInt: bytes);
if (res != 123456)
return Tuple.Create(false, "The int returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");
}
#endregion
}
}
79 changes: 79 additions & 0 deletions SmokeTests/FluentSyntax.cs
Original file line number Diff line number Diff line change
Expand Up @@ -963,6 +963,85 @@ private class TimespanResult
}
#endregion

#region Binary Data
[SmokeTest("Fluent Syntax Binary ResultSet")]
Tuple<bool, string> ExecuteBinarySync(IDbConnection db)
{
byte[] res = StoredProcedure.Create ("usp_GetIntAsBytes")
.WithParameter("toBytes", 42)
.WithResults<byte[]>()
.Execute(db, Program.timeout)
.Single();

if (BitConverter.IsLittleEndian)
Array.Reverse(res);

if (BitConverter.ToInt32(res, 0) != 42)
return Tuple.Create(false, "The bytes returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");
}

[SmokeTest("Fluent Syntax Binary ResultSet (await)")]
async Task<Tuple<bool, string>> ExecuteBinaryAsync(IDbConnection db)
{
byte[] res = (await StoredProcedure.Create ("usp_GetIntAsBytes")
.WithParameter("toBytes", 42)
.WithResults<byte[]>()
.ExecuteAsync(db, Program.timeout))
.Single();
if (BitConverter.IsLittleEndian)
Array.Reverse(res);

if (BitConverter.ToInt32(res, 0) != 42)
return Tuple.Create(false, "The bytes returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");
}

[SmokeTest("Fluent Syntax Binary ResultSet (task)")]
Task<Tuple<bool, string>> ExecuteBinaryTask(IDbConnection db)
{
var t = StoredProcedure.Create ("usp_GetIntAsBytes")
.WithParameter("toBytes", 42)
.WithResults<byte[]>()
.ExecuteAsync(db, Program.timeout);

return t.ContinueWith(r =>
{
var res = r.Result.Single();

if (BitConverter.IsLittleEndian)
Array.Reverse(res);

if (BitConverter.ToInt32(res, 0) != 42)
return Tuple.Create(false, "The bytes returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");

});
}

[SmokeTest("Fluent Syntax Binary Parameter")]
Tuple<bool, string> ExecuteBinaryParameter(IDbConnection db)
{
byte[] bytes = BitConverter.GetBytes(123456);
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);

int res = StoredProcedure.Create("usp_GetBytesAsInt")
.WithParameter("toInt", bytes)
.WithResults<int>()
.Execute(db, Program.timeout)
.Single();

if (res != 123456)
return Tuple.Create(false, "The int returned from the stored procedure did not match the expected results");

return Tuple.Create(true, "");
}
#endregion

private class DoublingTransformer : IDataTransformer<int>, IDataTransformer<Spoke>
{
public bool CanTransform(object value, Type targetType, bool isNullable, IEnumerable<Attribute> propertyAttributes)
Expand Down
Binary file modified SmokeTests/Smoke.mdf
Binary file not shown.
Binary file modified SmokeTests/Smoke_log.ldf
Binary file not shown.
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 2.3.0.{build}
version: 2.4.0.{build}
skip_tags: false

# Operating system (build VM template)
Expand All @@ -10,7 +10,7 @@ branches:
- gh-pages

init:
- ps: $env:releaseVersion = "2.3.0"
- ps: $env:releaseVersion = "2.4.0"
- ps: $env:packageVersion = if (Test-Path env:\APPVEYOR_REPO_TAG_NAME) { "$env:APPVEYOR_REPO_TAG_NAME" } else { "{0}-aInternalBuild{1:000}" -f $env:releaseVersion, [System.Int32]::Parse($env:APPVEYOR_BUILD_NUMBER) }

assembly_info:
Expand Down

0 comments on commit aa3ae4b

Please sign in to comment.