Skip to content

Commit

Permalink
#854 More fixes in the compiler area.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikependon committed Sep 10, 2021
1 parent 4060208 commit 41762f9
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 47 deletions.
1 change: 1 addition & 0 deletions RepoDb.Core/RepoDb/Extensions/DbCommandExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ private static IDbDataParameter CreateParameterForEnum(IDbCommand command,

// DbType
var dbType = IsUserDefined(dbField) ? default :
classProperty.GetDbType() ??
valueType.GetDbType() ??
(dbField != null ? clientTypeToDbTypeResolver.Resolve(dbField.Type) : null) ??
(DbType?)Converter.EnumDefaultDatabaseType;
Expand Down
23 changes: 22 additions & 1 deletion RepoDb.Core/RepoDb/Reflection/Compiler/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,16 @@ internal static Expression GetEntityInstancePropertyValueExpression(Expression e
private static bool IsUserDefined(DbField dbField) =>
string.Equals(dbField?.DatabaseType, "USER-DEFINED", StringComparison.OrdinalIgnoreCase);

/// <summary>
///
/// </summary>
/// <param name="size"></param>
/// <param name="dbField"></param>
private static int GetSize(int? size,
DbField dbField) =>
size.HasValue ? size.Value :
dbField?.Size.HasValue == true ? dbField.Size.Value : default;

/// <summary>
///
/// </summary>
Expand Down Expand Up @@ -1673,10 +1683,21 @@ internal static MethodCallExpression GetDbParameterDirectionAssignmentExpression
/// <param name="size"></param>
/// <returns></returns>
internal static MethodCallExpression GetDbParameterSizeAssignmentExpression(ParameterExpression parameterVariableExpression,
int size) =>
GetDbParameterSizeAssignmentExpression((Expression)parameterVariableExpression, size);

/// <summary>
///
/// </summary>
/// <param name="parameterVariableExpression"></param>
/// <param name="size"></param>
/// <returns></returns>
internal static MethodCallExpression GetDbParameterSizeAssignmentExpression(Expression parameterVariableExpression,
int size)
{
var parameterExpression = ConvertEnumExpressionToTypeExpression(parameterVariableExpression, StaticType.DbParameter);
var dbParameterSizeSetMethod = StaticType.DbParameter.GetProperty("Size").SetMethod;
return Expression.Call(parameterVariableExpression, dbParameterSizeSetMethod, Expression.Constant(size));
return Expression.Call(parameterExpression, dbParameterSizeSetMethod, Expression.Constant(size));
}

/// <summary>
Expand Down
162 changes: 118 additions & 44 deletions RepoDb.Core/RepoDb/Reflection/Compiler/PlainTypeToDbParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using RepoDb.Resolvers;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
Expand Down Expand Up @@ -39,63 +40,43 @@ internal static Action<DbCommand, object> GetPlainTypeToDbParametersCompiledFunc
// Variables
var dbField = dbFields?.FirstOrDefault(df =>
string.Equals(df.Name, paramProperty.GetMappedName(), StringComparison.OrdinalIgnoreCase));
var valueExpression = (Expression)Expression.Property(entityExpression, paramProperty.PropertyInfo);
var targetProperty = (entityProperty ?? paramProperty);
var valueType = targetProperty.PropertyInfo.PropertyType.GetUnderlyingType();
var valueExpression = (Expression)Expression.Property(entityExpression, paramProperty.PropertyInfo);
var parameterExpression = (Expression)null;

// Enum
if (valueType.IsEnum && dbField != null)
{
var valueTypeDbType = valueType.GetDbType();
var enumToType = (valueTypeDbType != null) ? new DbTypeToClientTypeResolver().Resolve(valueTypeDbType.Value) : dbField.Type;
valueExpression = ConvertEnumExpressionToTypeExpression(valueExpression, enumToType);
}

// PropertyHandler
var (convertedExpression, handlerSetReturnType) = ConvertExpressionToPropertyHandlerSetExpressionTuple(valueExpression, paramProperty, valueType);
if (handlerSetReturnType != null)
{
valueExpression = convertedExpression;
valueType = handlerSetReturnType;
}
// Property Handler
InvokePropertyHandlerViaExpression(paramProperty, ref valueType, ref valueExpression);

// Automatic
if (Converter.ConversionType == ConversionType.Automatic && dbField?.Type != null)
// Create
if (valueType.IsEnum)
{
valueType = dbField.Type.GetUnderlyingType();
valueExpression = ConvertExpressionWithAutomaticConversion(valueExpression, valueType);
parameterExpression = GetPlainTypeToDbParametersForEnumCompiledFunction(commandParameterExpression,
paramProperty,
dbField,
valueType,
valueExpression);
}

// DbType (retry since the type might had changed after all)
var dbType =
(paramProperty == null ? null : TypeMapCache.Get(paramProperty.GetDeclaringType(), paramProperty.PropertyInfo)) ??
(entityProperty == null ? null : TypeMapCache.Get(entityProperty.GetDeclaringType(), entityProperty.PropertyInfo));
if (dbType == null && (valueType ??= dbField?.Type.GetUnderlyingType()) != null)
else
{
var resolver = new ClientTypeToDbTypeResolver();
dbType =
valueType.GetDbType() ?? // type level, use TypeMapCache
resolver.Resolve(valueType) ?? // type level, primitive mapping
(valueType.IsEnum ?
(dbField?.Type != null ? resolver.Resolve(dbField.Type) : null) ?? // use the DBField.Type
Converter.EnumDefaultDatabaseType : null); // use Converter.EnumDefaultDatabaseType
parameterExpression = GetPlainTypeToDbParametersForNonEnumCompiledFunction(commandParameterExpression,
paramProperty,
entityProperty,
dbField,
valueType,
valueExpression);
}

var dbTypeExpression = dbType == null ? GetNullableTypeExpression(StaticType.DbType) :
ConvertExpressionToNullableExpression(Expression.Constant(dbType), StaticType.DbType);
// Size
var sizeExpression = GetDbParameterSizeAssignmentExpression(parameterExpression, GetSize(null, dbField));
callExpressions.Add(sizeExpression);

// DbCommandExtension.CreateParameter
var expression = Expression.Call(methodInfo, new Expression[]
{
commandParameterExpression,
Expression.Constant(paramProperty.GetMappedName()),
ConvertExpressionToTypeExpression(valueExpression, StaticType.Object),
dbTypeExpression
});
// Type map attributes
//InvokeParameterPropertyValueSetterAttributes(parametersExpression, paramProperty);

// DbCommand.Parameters.Add
var parametersExpression = Expression.Property(commandParameterExpression, "Parameters");
var addExpression = Expression.Call(parametersExpression, GetDbParameterCollectionAddMethod(), expression);
var addExpression = Expression.Call(parametersExpression, GetDbParameterCollectionAddMethod(), parameterExpression);

// Add
callExpressions.Add(addExpression);
Expand All @@ -106,5 +87,98 @@ internal static Action<DbCommand, object> GetPlainTypeToDbParametersCompiledFunc
.Lambda<Action<DbCommand, object>>(Expression.Block(callExpressions), commandParameterExpression, entityParameterExpression)
.Compile();
}

public static Expression GetPlainTypeToDbParametersForNonEnumCompiledFunction(Expression commandParameterExpression,
ClassProperty paramProperty,
ClassProperty entityProperty,
DbField dbField,
Type valueType,
Expression valueExpression)
{
// Automatic
if (Converter.ConversionType == ConversionType.Automatic && dbField?.Type != null)
{
valueType = dbField.Type.GetUnderlyingType();
valueExpression = ConvertExpressionWithAutomaticConversion(valueExpression, valueType);
}

// DbType
var dbType = (paramProperty == null ? null : TypeMapCache.Get(paramProperty.GetDeclaringType(), paramProperty.PropertyInfo)) ??
(entityProperty == null ? null : TypeMapCache.Get(entityProperty.GetDeclaringType(), entityProperty.PropertyInfo));

valueType = (valueType ?? dbField?.Type.GetUnderlyingType());
if (dbType == null && (valueType ??= dbField?.Type.GetUnderlyingType()) != null)
{
var resolver = new ClientTypeToDbTypeResolver();
dbType =
valueType.GetDbType() ?? // type level, use TypeMapCache
resolver.Resolve(valueType) ?? // type level, primitive mapping
(dbField?.Type != null ?
resolver.Resolve(dbField?.Type) : null); // Fallback to the database type
}
var dbTypeExpression = dbType == null ? GetNullableTypeExpression(StaticType.DbType) :
ConvertExpressionToNullableExpression(Expression.Constant(dbType), StaticType.DbType);

// DbCommandExtension.CreateParameter
var methodInfo = GetDbCommandCreateParameterMethod();
return Expression.Call(methodInfo, new Expression[]
{
commandParameterExpression,
Expression.Constant(paramProperty.GetMappedName()),
ConvertExpressionToTypeExpression(valueExpression, StaticType.Object),
dbTypeExpression
});
}

/// <summary>
///
/// </summary>
/// <param name="commandParameterExpression"></param>
/// <param name="paramProperty"></param>
/// <param name="dbField"></param>
/// <param name="valueType"></param>
/// <param name="valueExpression"></param>
/// <returns></returns>
public static Expression GetPlainTypeToDbParametersForEnumCompiledFunction(Expression commandParameterExpression,
ClassProperty paramProperty,
DbField dbField,
Type valueType,
Expression valueExpression)
{
// DbType
var dbType = IsUserDefined(dbField) ? default :
paramProperty.GetDbType() ??
valueType.GetDbType() ??
(dbField != null ? new ClientTypeToDbTypeResolver().Resolve(dbField.Type) : null) ??
(DbType?)Converter.EnumDefaultDatabaseType;

// DbCommandExtension.CreateParameter
var methodInfo = GetDbCommandCreateParameterMethod();
return Expression.Call(methodInfo, new Expression[]
{
commandParameterExpression,
Expression.Constant(paramProperty.GetMappedName()),
ConvertExpressionToTypeExpression(valueExpression, StaticType.Object),
ConvertExpressionToNullableExpression(Expression.Constant(dbType), StaticType.DbType)
});
}

/// <summary>
///
/// </summary>
/// <param name="classProperty"></param>
/// <param name="valueType"></param>
/// <param name="valueExpression"></param>
public static void InvokePropertyHandlerViaExpression(ClassProperty classProperty,
ref Type valueType,
ref Expression valueExpression)
{
var (expression, type) = ConvertExpressionToPropertyHandlerSetExpressionTuple(valueExpression, classProperty, valueType);
if (type != null)
{
valueType = type;
valueExpression = expression;
}
}
}
}
21 changes: 19 additions & 2 deletions RepoDb.PostgreSql/RepoDb.PostgreSql.IntegrationTests/EnumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public void TestInsertAndQueryEnumAsEnumAsBatch()
}

[TestMethod]
public void TestInsertAndQueryEnumAsEnumByEnum()
public void TestInsertAndQueryEnumAsEnumViaEnum()
{
using (var connection = new NpgsqlConnection(Database.ConnectionString))
{
Expand All @@ -379,6 +379,24 @@ public void TestInsertAndQueryEnumAsEnumByEnum()
}
}

[TestMethod]
public void TestInsertAndQueryEnumAsEnumViaDynamicEnum()
{
using (var connection = new NpgsqlConnection(Database.ConnectionString))
{
// Setup
var person = GetPersonWithEnum(1).First();

// Act
connection.Insert(person);

// Query
var queryResult = connection.Query<PersonWithEnum>(new { ColumnEnum = person.ColumnEnum }).First();

// Assert
Assert.AreEqual(person.ColumnEnum, queryResult.ColumnEnum);
}
}
[TestMethod]
public void TestInsertAndQueryEnumAsNullableEnumAsNull()
{
Expand Down Expand Up @@ -459,6 +477,5 @@ public void TestInsertAndQueryEnumAsNullableEnumByEnum()
Assert.AreEqual(person.ColumnEnum, queryResult.ColumnEnum);
}
}

}
}

0 comments on commit 41762f9

Please sign in to comment.