Skip to content

Commit

Permalink
Initial support for Enumerations : document enum support #261
Browse files Browse the repository at this point in the history
  • Loading branch information
mikependon committed Jun 6, 2019
1 parent 4c47a6c commit 5d7103e
Show file tree
Hide file tree
Showing 46 changed files with 2,410 additions and 128 deletions.
659 changes: 659 additions & 0 deletions RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/EnumPropertyTest.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace RepoDb.IntegrationTests.Enumerations
{
public enum BooleanValue
{
False,
True
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace RepoDb.IntegrationTests.Enumerations
{
public enum Continent
{
Asia,
Africa,
Australia,
Europe,
NorthAmerica,
SouthAmerica,
Antartica
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace RepoDb.IntegrationTests.Enumerations
{
public enum Direction
{
North,
South,
East,
West
}
}
120 changes: 120 additions & 0 deletions RepoDb.Core/RepoDb.Tests/RepoDb.IntegrationTests/Helper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RepoDb.Extensions;
using RepoDb.IntegrationTests.Enumerations;
using RepoDb.IntegrationTests.Models;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -356,6 +357,125 @@ public static IdentityTableWithDifferentPrimary CreateIdentityTableWithDifferent

#endregion

#region EnumCompleteTable

/// <summary>
/// Creates a list of <see cref="EnumCompleteTable"/> objects.
/// </summary>
/// <param name="count">The number of rows.</param>
/// <returns>A list of <see cref="EnumCompleteTable"/> objects.</returns>
public static List<EnumCompleteTable> CreateEnumCompleteTables(int count)
{
var tables = new List<EnumCompleteTable>();
for (var i = 0; i < count; i++)
{
var index = i + 1;
tables.Add(new EnumCompleteTable
{
SessionId = Guid.NewGuid(),
ColumnBit = BooleanValue.True,
ColumnNVarChar = Direction.West,
ColumnInt = Direction.West,
ColumnBigInt = Direction.West,
ColumnSmallInt = Direction.West
});
}
return tables;
}

/// <summary>
/// Creates an instance of <see cref="EnumCompleteTable"/> object.
/// </summary>
/// <returns>A new created instance of <see cref="EnumCompleteTable"/> object.</returns>
public static EnumCompleteTable CreateEnumCompleteTable()
{
return new EnumCompleteTable
{
SessionId = Guid.NewGuid(),
ColumnBit = BooleanValue.True,
ColumnNVarChar = Direction.West,
ColumnInt = Direction.West,
ColumnBigInt = Direction.West,
ColumnSmallInt = Direction.West
};
}

#endregion

#region EnumAsIntForStringCompleteTable

/// <summary>
/// Creates a list of <see cref="EnumAsIntForStringCompleteTable"/> objects.
/// </summary>
/// <param name="count">The number of rows.</param>
/// <returns>A list of <see cref="EnumAsIntForStringCompleteTable"/> objects.</returns>
public static List<EnumAsIntForStringCompleteTable> CreateEnumAsIntForStringCompleteTables(int count)
{
var tables = new List<EnumAsIntForStringCompleteTable>();
for (var i = 0; i < count; i++)
{
var index = i + 1;
tables.Add(new EnumAsIntForStringCompleteTable
{
SessionId = Guid.NewGuid(),
ColumnNVarChar = Direction.West
});
}
return tables;
}

/// <summary>
/// Creates an instance of <see cref="EnumAsIntForStringCompleteTable"/> object.
/// </summary>
/// <returns>A new created instance of <see cref="EnumAsIntForStringCompleteTable"/> object.</returns>
public static EnumAsIntForStringCompleteTable CreateEnumAsIntForStringCompleteTable()
{
return new EnumAsIntForStringCompleteTable
{
SessionId = Guid.NewGuid(),
ColumnNVarChar = Direction.West
};
}

#endregion

#region TypeLevelMappedForStringEnumCompleteTable

/// <summary>
/// Creates a list of <see cref="TypeLevelMappedForStringEnumCompleteTable"/> objects.
/// </summary>
/// <param name="count">The number of rows.</param>
/// <returns>A list of <see cref="TypeLevelMappedForStringEnumCompleteTable"/> objects.</returns>
public static List<TypeLevelMappedForStringEnumCompleteTable> CreateTypeLevelMappedForStringEnumCompleteTables(int count)
{
var tables = new List<TypeLevelMappedForStringEnumCompleteTable>();
for (var i = 0; i < count; i++)
{
var index = i + 1;
tables.Add(new TypeLevelMappedForStringEnumCompleteTable
{
SessionId = Guid.NewGuid(),
ColumnNVarChar = Continent.Asia
});
}
return tables;
}

/// <summary>
/// Creates an instance of <see cref="TypeLevelMappedForStringEnumCompleteTable"/> object.
/// </summary>
/// <returns>A new created instance of <see cref="TypeLevelMappedForStringEnumCompleteTable"/> object.</returns>
public static TypeLevelMappedForStringEnumCompleteTable CreateTypeLevelMappedForStringEnumCompleteTable()
{
return new TypeLevelMappedForStringEnumCompleteTable
{
SessionId = Guid.NewGuid(),
ColumnNVarChar = Continent.Asia
};
}

#endregion

#region Dynamics

#region IdentityTable
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using RepoDb.Attributes;
using RepoDb.IntegrationTests.Enumerations;
using System;
using System.Data;

namespace RepoDb.IntegrationTests.Models
{
[Map("[dbo].[CompleteTable]")]
public class EnumCompleteTable
{
public Guid SessionId { get; set; }
public BooleanValue? ColumnBit { get; set; }
public Direction ColumnNVarChar { get; set; }
public Direction? ColumnInt { get; set; }
public Direction? ColumnBigInt { get; set; }
public Direction? ColumnSmallInt { get; set; }
}

[Map("[dbo].[CompleteTable]")]
public class EnumAsIntForStringCompleteTable
{
public Guid SessionId { get; set; }
[TypeMap(DbType.Int32)]
public Direction ColumnNVarChar { get; set; }
}

[Map("[dbo].[CompleteTable]")]
public class TypeLevelMappedForStringEnumCompleteTable
{
public Guid SessionId { get; set; }
public Continent ColumnNVarChar { get; set; }
}
}
21 changes: 18 additions & 3 deletions RepoDb.Core/RepoDb/ClassProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,25 @@ public IdentityAttribute GetIdentityAttribute()
{
return m_dbType;
}

// Set the flag
m_isDbTypeWasSet = true;
return m_dbType = PropertyInfo.GetCustomAttribute<TypeMapAttribute>()?.DbType ??
TypeMapper.Get(PropertyInfo.PropertyType.GetUnderlyingType()) ??
m_clientTypeToSqlDbTypeResolver.Resolve(PropertyInfo.PropertyType);

// Get the type (underlying type)
var propertyType = PropertyInfo.PropertyType.GetUnderlyingType();

// Property and Type level mapping
m_dbType = PropertyInfo.GetCustomAttribute<TypeMapAttribute>()?.DbType ??
TypeMapper.Get(propertyType);

// Try to resolve if not found
if (m_dbType == null && propertyType.GetTypeInfo().IsEnum == false)
{
m_dbType = m_clientTypeToSqlDbTypeResolver.Resolve(PropertyInfo.PropertyType);
}

// Return the value
return m_dbType;
}

/*
Expand Down
69 changes: 59 additions & 10 deletions RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,25 @@ public static void CreateParameters(this IDbCommand command,
// Get the property vaues
var name = property.GetUnquotedMappedName();
var value = property.PropertyInfo.GetValue(param);
var dbType = property.GetDbType() ??
TypeMapper.Get(property.PropertyInfo.PropertyType.GetUnderlyingType() ??
value?.GetType().GetUnderlyingType());

// Ensure the type mapping
// Get property db type
var dbType = property.GetDbType();

// Ensure mapping based on the value type
if (dbType == null)
{
dbType = TypeMapper.Get(value?.GetType().GetUnderlyingType());
}

// Check for specialized
if (dbType == null)
{
if (value == null && property.PropertyInfo.PropertyType == m_bytesType)
var propertyType = property.PropertyInfo.PropertyType.GetUnderlyingType();
if (propertyType?.GetTypeInfo().IsEnum == true)
{
dbType = DbType.String;
}
else if (propertyType == m_bytesType)
{
dbType = DbType.Binary;
}
Expand Down Expand Up @@ -244,6 +255,7 @@ private static void CreateParameters(this IDbCommand command,
foreach (var kvp in kvps)
{
var value = kvp.Value;
var valueType = (Type)null;

// Cast the proper object and identify the properties
if (kvp.Value is CommandParameter)
Expand All @@ -255,13 +267,36 @@ private static void CreateParameters(this IDbCommand command,
value = commandParameter.Value;

// Get the DB Type
dbType = property?.GetCustomAttribute<TypeMapAttribute>()?.DbType ??
TypeMapper.Get(property?.PropertyType?.GetUnderlyingType() ??
kvp.Value?.GetType()?.GetUnderlyingType());
dbType = property?.GetCustomAttribute<TypeMapAttribute>()?.DbType;

// Get the property-level mapping
if (property != null)
{
dbType = TypeMapper.Get(property.PropertyType.GetUnderlyingType());
}

// Set the value type if the DB Type is not found
if (dbType == null)
{
valueType = value?.GetType()?.GetUnderlyingType();
}
}
else
{
dbType = TypeMapper.Get(kvp.Value?.GetType()?.GetUnderlyingType());
valueType = kvp.Value?.GetType()?.GetUnderlyingType();
}

// Check for the specialized types
if (dbType == null)
{
if (valueType?.GetTypeInfo().IsEnum == true)
{
dbType = DbType.String;
}
else if (valueType == m_bytesType)
{
dbType = DbType.Binary;
}
}

// Add the parameter
Expand Down Expand Up @@ -328,7 +363,21 @@ private static void CreateParameters(this IDbCommand command,

// Get the values
var value = queryField.Parameter.Value;
var dbType = TypeMapper.Get(value?.GetType()?.GetUnderlyingType());
var valueType = value?.GetType()?.GetUnderlyingType();
var dbType = TypeMapper.Get(valueType);

// Check for the specialized types
if (dbType == null)
{
if (valueType?.GetTypeInfo().IsEnum == true)
{
dbType = DbType.String;
}
else if (valueType == m_bytesType)
{
dbType = DbType.Binary;
}
}

// Create the parameter
command.Parameters.Add(command.CreateParameter(queryField.Parameter.Name, value, dbType));
Expand Down
13 changes: 12 additions & 1 deletion RepoDb.Core/RepoDb/Extensions/DbConnectionExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,12 +1032,23 @@ internal static async Task<TResult> ExecuteScalarAsyncInternal<TResult>(this IDb
/// Throws an exception if there is no defined primary key on the data entity type.
/// </summary>
/// <typeparam name="TEntity">The type of the data entity object.</typeparam>
/// <param name="connection">The connection object to be used.</param>
/// <returns>The primary <see cref="ClassProperty"/> of the type.</returns>
private static ClassProperty GetAndGuardPrimaryKey<TEntity>()
private static ClassProperty GetAndGuardPrimaryKey<TEntity>(IDbConnection connection)
where TEntity : class
{
var property = PrimaryCache.Get<TEntity>();
if (property == null)
{
var dbFields = DbFieldCache.Get(connection, ClassMappedNameCache.Get<TEntity>());
var primary = dbFields.FirstOrDefault(df => df.IsPrimary);
if (primary != null)
{
var properties = PropertyCache.Get<TEntity>();
property = properties.FirstOrDefault(p => string.Equals(p.GetUnquotedMappedName(), primary.UnquotedName, StringComparison.OrdinalIgnoreCase));
}
}
if (property == null)
{
throw new PrimaryFieldNotFoundException($"No primary key found at type '{typeof(TEntity).FullName}'.");
}
Expand Down
8 changes: 4 additions & 4 deletions RepoDb.Core/RepoDb/Operations/DbConnection/Delete.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public static int Delete<TEntity>(this IDbConnection connection,
IStatementBuilder statementBuilder = null)
where TEntity : class
{
var primary = GetAndGuardPrimaryKey<TEntity>();
var primary = GetAndGuardPrimaryKey<TEntity>(connection);
return Delete<TEntity>(connection: connection,
where: ToQueryGroup<TEntity>(primary, entity),
commandTimeout: commandTimeout,
Expand Down Expand Up @@ -64,7 +64,7 @@ public static int Delete<TEntity>(this IDbConnection connection,
IStatementBuilder statementBuilder = null)
where TEntity : class
{
GetAndGuardPrimaryKey<TEntity>();
GetAndGuardPrimaryKey<TEntity>(connection);
return Delete<TEntity>(connection: connection,
where: WhereOrPrimaryKeyToQueryGroup<TEntity>(whereOrPrimaryKey),
commandTimeout: commandTimeout,
Expand Down Expand Up @@ -245,7 +245,7 @@ public static Task<int> DeleteAsync<TEntity>(this IDbConnection connection,
IStatementBuilder statementBuilder = null)
where TEntity : class
{
var primary = GetAndGuardPrimaryKey<TEntity>();
var primary = GetAndGuardPrimaryKey<TEntity>(connection);
return DeleteAsync<TEntity>(connection: connection,
where: ToQueryGroup<TEntity>(primary, entity),
commandTimeout: commandTimeout,
Expand Down Expand Up @@ -273,7 +273,7 @@ public static Task<int> DeleteAsync<TEntity>(this IDbConnection connection,
IStatementBuilder statementBuilder = null)
where TEntity : class
{
GetAndGuardPrimaryKey<TEntity>();
GetAndGuardPrimaryKey<TEntity>(connection);
return DeleteAsync<TEntity>(connection: connection,
where: WhereOrPrimaryKeyToQueryGroup<TEntity>(whereOrPrimaryKey),
commandTimeout: commandTimeout,
Expand Down
Loading

0 comments on commit 5d7103e

Please sign in to comment.