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 ??= dbField?.Type.GetUnderlyingType(); if (dbType == null && valueType != 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> /// Create Parameter, the process will handle value conversion and type conversion. /// </summary> /// <param name="command">The command object to be used.</param> /// <param name="name">Entity property's name.</param> /// <param name="value">Entity property's value, maybe null.</param> /// <param name="classProperty"> /// The entity's class property information. <br /> /// If the parameter is a dictionary, it will be null, otherwise it will not be null. /// </param> /// <param name="dbField"> /// Used to get the actual field type information of the database to determine whether automatic type conversion is required. <br /> /// When the tableName is assigned, it will be based on the database field information obtained by the tableName, so this parameter will be null in most cases. /// </param> /// <param name="parameterDirection">The direction of the parameter.</param> /// <param name="fallbackType">Used when none of the above parameters can determine the value of Parameter.DbType.</param> /// <returns>An instance of the newly created parameter object.</returns> private static IDbDataParameter CreateParameter(IDbCommand command, string name, object value, ClassProperty classProperty, DbField dbField, ParameterDirection?parameterDirection, Type fallbackType) { /* * In some cases, the value type and the classProperty type will be inconsistent, Therefore, the type gives priority to value. * ex: in RepoDb.MySql.IntegrationTests.TestMySqlConnectionForQueryForMySqlMapAttribute * entity AttributeTable.Id = int * database completetable.Id = bigint(20) AUTO_INCREMENT * value id in connection.Query<AttributeTable>(id) is from connection.Insert<AttributeTable>(table) = ulong */ var valueType = (value?.GetType() ?? classProperty?.PropertyInfo.PropertyType).GetUnderlyingType(); // Enum if (valueType?.IsEnum == true && dbField != null) { value = ConvertEnumValueToType(value, dbField.Type); } /* * Try to get the propertyHandler, the order of the source of resolve is classProperty and valueType. * If the propertyHandler exists, the value and DbType are re-determined by the propertyHandler. */ var propertyHandler = classProperty?.GetPropertyHandler() ?? (valueType == null ? null : PropertyHandlerCache.Get <object>(valueType)); if (propertyHandler != null) { var propertyHandlerSetMethod = propertyHandler.GetType().GetMethod("Set"); value = propertyHandlerSetMethod.Invoke(propertyHandler, new[] { value, classProperty }); valueType = propertyHandlerSetMethod.ReturnType.GetUnderlyingType(); } /* * When the database field information exists and the field type definition is found to be different from the valueType, * if automatic type conversion is activated at this time, it will be processed. */ if (valueType != null && dbField != null && IsAutomaticConversion(dbField)) { var dbFieldType = dbField.Type.GetUnderlyingType(); if (dbFieldType != valueType) { if (value != null) { value = AutomaticConvert(value, valueType, dbFieldType); } valueType = dbFieldType; } } /* * Set DbType as much as possible, to avoid parameter misjudgment when Value is null. * order: * 1. attribute level, TypeMapAttribute define on class's property * 2. property level, assigned by TypeMapper.Add(entityType, property, dbType, ...) * 3. type level, use TypeMapCache, assigned by TypeMapper.Add(type, dbType, ...), not included Primitive mapping. * 4. type level, primitive mapping, included special type. ex: byte[] ...etc. * 5. if isEnum, use Converter.EnumDefaultDatabaseType. */ // if classProperty exists, try get dbType from attribute level or property level, // The reason for not using classProperty.GetDbType() is to avoid getting the type level mapping. var dbType = classProperty == null ? null : TypeMapCache.Get(classProperty.GetDeclaringType(), classProperty.PropertyInfo); if (dbType == null && (valueType ??= dbField?.Type.GetUnderlyingType() ?? fallbackType) != null) { dbType = valueType.GetDbType() ?? // type level, use TypeMapCache clientTypeToDbTypeResolver.Resolve(valueType) ?? // type level, primitive mapping (dbField?.Type != null ? clientTypeToDbTypeResolver.Resolve(dbField?.Type) : null); // fallback to the database type } if (dbType == null && valueType.IsEnum) { dbType = Converter.EnumDefaultDatabaseType; // use Converter.EnumDefaultDatabaseType } /* * Return the parameter */ return(command.CreateParameter(name, value, dbType, parameterDirection)); }