schotime / NPoco

Simple microORM that maps the results of a query onto a POCO object. Project based on Schotime's branch of PetaPoco
Apache License 2.0
848 stars 302 forks source link

Custom Mapper works but NPoco despite this throws #632

Open dario-l opened 3 years ago

dario-l commented 3 years ago

"NPoco.SqlServer" Version="5.1.2"

I have custom mapper for complex object. Mapper serializes object to JSON and vice-versa. Mapper works (serialization and deserialization returns proper values from/to db).

Exception is odd:

NullReferenceException: Object reference not set to an instance of an object.

    NPoco.RowMappers.PropertyMapper.MapValue(GroupResult<PosName> posName, object[] values, Func<object, object> converter, object instance, PocoColumn pocoColumn, object defaultValue)
    NPoco.RowMappers.PropertyMapper+<>c__DisplayClass8_2.<BuildMapPlans>b__3(DbDataReader reader, object[] values, object instance)
    NPoco.RowMappers.PropertyMapper+<>c__DisplayClass7_0.<BuildMapPlan>b__1(DbDataReader reader, object[] values, object instance)
    NPoco.RowMappers.PropertyMapper.Map(DbDataReader dataReader, RowMapperContext context)
    NPoco.MappingFactory.Map(DbDataReader dataReader, object instance)
    NPoco.Database.ReadAsync<T>(object instance, DbDataReader r, PocoData pd)
    NPoco.Database.ReadAsync<T>(object instance, DbDataReader r, PocoData pd)
    NPoco.Database.QueryAsync<T>(T instance, Expression<Func<T, IList>> listExpression, Func<T, object[]> idFunc, Sql Sql, PocoData pocoData)
    NPoco.Database.QueryAsync<T>(T instance, Expression<Func<T, IList>> listExpression, Func<T, object[]> idFunc, Sql Sql, PocoData pocoData)
    NPoco.Database.QueryAsync<T>(T instance, Expression<Func<T, IList>> listExpression, Func<T, object[]> idFunc, Sql Sql, PocoData pocoData)
    System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<TResult>+ConfiguredValueTaskAwaiter.GetResult()
    System.Linq.AsyncEnumerable.<SingleOrDefaultAsync>g__Core|534_0<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken) in SingleOrDefault.cs
    System.Linq.AsyncEnumerable.<SingleOrDefaultAsync>g__Core|534_0<TSource>(IAsyncEnumerable<TSource> source, CancellationToken cancellationToken) in SingleOrDefault.cs

Mapper:

    public class ReferenceCollectionMapper : DefaultMapper
    {
        private readonly IColumnSerializer _serializer = DatabaseFactory.ColumnSerializer;

        public override Func<object, object> GetFromDbConverter(Type destType, Type sourceType)
        {
            var isReferenceCollection = IsReferenceCollection(destType);

            return isReferenceCollection
                ? o => o == null ? null : _serializer.Deserialize((string)o, typeof(ReferenceCollection))
                : base.GetFromDbConverter(destType, sourceType);
        }

        public override Func<object, object> GetToDbConverter(Type destType, MemberInfo sourceMemberInfo)
        {
            var sourceType = sourceMemberInfo.GetMemberInfoType();

            return IsReferenceCollection(sourceType)
                ? o => o == null ? null : _serializer.Serialize(o)
                : base.GetToDbConverter(destType, sourceType);
        }

        private static bool IsReferenceCollection(Type type) => type == typeof(ReferenceCollection);
    }

One more thing. Method GetFromDbConverter(MemberInfo destMemberInfo, Type sourceType) is never invoked for member type ReferenceCollection. 😞

dario-l commented 3 years ago

It turns out that for simple clr types like int, int[], List<int> works but doesn't work when it is custom object type, for eg.: ReferenceCollection.

    public class ReferenceCollection
    {
        public List<Item> Items { get; private init; } = new();

        public class Item
        {
            public int Id { get; private init; }
            public string Name { get; private init; }

            public static Item Create(int id, string name)
            {
                return new()
                {
                    Id = id,
                    Name = name
                };
            }
        }
    }