mikependon / RepoDB

A hybrid ORM library for .NET.
Apache License 2.0
1.7k stars 126 forks source link

Bug: Null handling not working correctly when mapping to object #872

Closed kiwidude68 closed 3 years ago

kiwidude68 commented 3 years ago

Bug Description

There is an issue with the null handling in column values when mapping to objects, as per my simple example below. running SL as simple as: SELECT 1 AS ResultCode, NULL as Message

and trying to map that to a result object that has int and string fields. The issue seems to be with the NULL. If I change the SQL to SELECT 1 AS ResultCode, '' as Message then the mapping will work.

Exception Message:

Failed to get result: System.InvalidOperationException: Compiler.DataReader.IsDbNull.FalseExpression: Failed to convert the value expression into its destination .NET CLR Type 'System.String'. PropertyInfo: Message (System.String), DeclaringType: RepoDbTest.Result
 ---> System.InvalidOperationException: No coercion operator is defined between types 'System.Int32' and 'System.String'.
   at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type)
   at RepoDb.Reflection.Compiler.ConvertExpressionToTypeExpression(Expression expression, Type toType)
   at RepoDb.Reflection.Compiler.GetClassPropertyParameterInfoIsDbNullFalseValueExpression(ParameterExpression readerParameterExpression, ClassPropertyParameterInfo classPropertyParameterInfo, DataReaderField readerField)
   --- End of inner exception stack trace ---
   at RepoDb.Reflection.Compiler.GetClassPropertyParameterInfoIsDbNullFalseValueExpression(ParameterExpression readerParameterExpression, ClassPropertyParameterInfo classPropertyParameterInfo, DataReaderField readerField)
   at RepoDb.Reflection.Compiler.GetClassPropertyParameterInfoValueExpression(ParameterExpression readerParameterExpression, ClassPropertyParameterInfo classPropertyParameterInfo, DataReaderField readerField)
   at RepoDb.Reflection.Compiler.GetMemberBindingsForDataEntity[TResult](ParameterExpression readerParameterExpression, IEnumerable`1 readerFields, IDbSetting dbSetting)
   at RepoDb.Reflection.Compiler.CompileDataReaderToDataEntity[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.Reflection.Compiler.CompileDataReaderToType[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.Reflection.FunctionFactory.CompileDataReaderToType[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.FunctionCache.DataReaderToTypeCache`1.Get(DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.FunctionCache.GetDataReaderToTypeCompiledFunction[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)
   at RepoDb.Reflection.DataReader.ToEnumerable[TResult](DbDataReader reader, IEnumerable`1 dbFields, IDbSetting dbSetting)+MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at RepoDb.Extensions.EnumerableExtension.AsList[T](IEnumerable`1 value)
   at RepoDb.DbConnectionExtension.ExecuteQueryInternalForType[TResult](IDbConnection connection, String commandText, Object param, Nullable`1 commandType, String cacheKey, Nullable`1 cacheItemExpiration, Nullable`1 commandTimeout, IDbTransaction transaction, ICache cache, String tableName, Boolean skipCommandArrayParametersCheck)
   at RepoDb.DbConnectionExtension.ExecuteQueryInternal[TResult](IDbConnection connection, String commandText, Object param, Nullable`1 commandType, String cacheKey, Nullable`1 cacheItemExpiration, Nullable`1 commandTimeout, IDbTransaction transaction, ICache cache, String tableName, Boolean skipCommandArrayParametersCheck)
   at RepoDb.DbConnectionExtension.ExecuteQuery[TResult](IDbConnection connection, String commandText, Object param, Nullable`1 commandType, String cacheKey, Nullable`1 cacheItemExpiration, Nullable`1 commandTimeout, IDbTransaction transaction, ICache cache)
   at RepoDbTest.Program.Main(String[] args)

Example Code:

using System;
using System.Data;
using System.Data.SqlClient;
using RepoDb;

namespace RepoDbTest
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlServerBootstrap.Initialize();
            string conn = "Server=.;Trusted_Connection=True;MultipleActiveResultSets=true";
            try
            {
                using (IDbConnection connection = new SqlConnection(conn).EnsureOpen())
                {
                    var result = connection.ExecuteQuery<Result>("SELECT 1 AS ResultCode, NULL as Message");
                    Console.WriteLine("Successfully got result");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed to get result: " + e);
            }
        }
    }

    public class Result
    {
        public int ResultCode { get; set; }
        public string Message { get; set; }
    }
}

Library Version:

Example: RepoDb v1.12.7 and RepoDb.SqlServer v1.1.3

kiwidude68 commented 3 years ago

Ignore this - as described in Gitter by Mike the issue is not giving a type to that SQL expression like (CONVERT(VARCHAR(50),NULL) AS Message) and by default RepoDb assumes it is an int column