Litedb: Document does not seem to support IReadOnlyCollection<<T>>

Created on 24 Dec 2016  路  10Comments  路  Source: mbdavid/LiteDB

A document class with the following property cannot be retrieved from a collection (throws exception)...

IReadOnlyCollection<> Property
{
get {...}
set{...}
}

The "set" code is never called when attempting to retrieve.

However, if I change the value type to ICollection<> instead, then it works.

bug fixed

All 10 comments

The type IEnumerable is also a problem. However, I get a strange exception when I try to retrieve an object with FindById():

Result Message:
System.ArgumentException : The value "System.Object" is not of type "TRAL.Trapi.PriceQuant" and cannot be used in this generic collection.
Parameter name: value

Result StackTrace:
at System.ThrowHelper.ThrowWrongValueTypeArgumentException(Object value, Type targetType)
at System.Collections.Generic.List1.System.Collections.IList.Add(Object item) at LiteDB.BsonMapper.DeserializeList(Type type, BsonArray value) at LiteDB.BsonMapper.Deserialize(Type type, BsonValue value) at LiteDB.BsonMapper.DeserializeObject(Type type, Object obj, BsonDocument value) at LiteDB.BsonMapper.Deserialize(Type type, BsonValue value) at LiteDB.BsonMapper.ToObject(Type type, BsonDocument doc) at LiteDB.BsonMapper.ToObject[T](BsonDocument doc) at LiteDB.LiteCollection1.d__17.MoveNext()
at System.Linq.Enumerable.SingleOrDefaultTSource

The "ThrowWrongValueTypeArgumentException" doesn't seem valid. Here is what my property looks like:

[code]

        /// <summary>
        /// Sorted list of "bid" price-quantities in descending order. A copy is made on assignment.
        /// </summary>
        public IEnumerable<PriceQuant> Bids
        {
            get { return _bids; }
            set
            {
                DEBUG.Out("BIDS");
                _bids = new SortedSet<PriceQuant>(value, _rev);
            }
        }

[/code]

The "PriceQuant" is a struct.

Simply switching to ICollection rather than IEnumerable or IReadOnlyCollection allows it to work.

Mapper doest not implement IReadOnlyCollection. Current version support only:

if (typeDef == typeof(IList<>) || 
    typeDef == typeof(ICollection<>) ||
    typeDef == typeof(IEnumerable<>))
{
    return CreateInstance(GetGenericListOfType(UnderlyingTypeOf(type)));
}

Any of this will create instance as List<T> to use Add method to insert data.

https://github.com/mbdavid/LiteDB/blob/master/LiteDB/Mapper/Reflection/Reflection.cs#L137

Also, you can register your own type using RegisterType<T> from BsonMapper.Global instance.

OK, understand about IReadOnlyCollection. Howver, this does not work with IEnumerable:

private static RevComparer _rev = new RevComparer();
private ICollection<PriceQuant> _bids = new SortedSet<PriceQuant>(_rev);

public IEnumerable<PriceQuant> Bids
{
            get { return _bids; }

            set
            {
                _bids = new SortedSet<PriceQuant>(value, _rev);
            }

}

However, simply switching to ICollection<<>> causes it to work.

PriceQuant is a simple struct with two double type properties.

Using a custom set in _bids = new SortedSet<PriceQuant>(value, _rev); still returing an IEnumerable and it's not possible use Add method.

When you change to IColleciton it's possible because ICollection contains Add.

Not sure understand.

You say that LiteCollection creates List :

Any of this will create instance as List to use Add method to insert data.

*List has the Add() method. *

Assigning a List object to my "Bids" property should work fine.

Yey, my last comment are wrong (i deleted). I marked this issue as "need review"... because it's need works.

I can add a little more information here.

I tested with a property of IEnumerable<double> and, bizarrely, it worked. IEnumerable<struct type> does not work however, whereas ICollection<struct type> does. This is strange!

I tested with the simpler code:

public struct TempStruct
{
    public double _a;
    public double _b;

    public TempStruct(double p)
    {
        _a = p;
        _b = 0;
    }
    public TempStruct(double p, double q)
    {
        _a = p;
        _b = q;
    }
}

...

Document:

    private IList<TempStruct> _temp = new List<TempStruct>();


    public int TempCount
    {
        get { return _temp.Count; }
    }
    public ICollection<TempStruct> TempList
    {
        get { return _temp; }
        set { _temp = new List<TempStruct>(value); }

    }

To test, I added 3 items and assigned to TempList.

This works fine. However, if we change to the following to:

public IEnumerable<TempStruct> TempList

It throws an exception when we deserialize.

System.ArgumentException : The value "System.Object" is not of type "TRAL.Trapi.TempStruct" and cannot be used in this generic collection.
Parameter name: value

This is strange indeed. Am i doing something wrong here?

PS. Making TempStruct a class and adding a default constructor does not work either. The result is the same.

Bingo! Method GetListItemType(Type listType) are not returning correct ItemType when listType are IEnumerable. Fixed.

Yeah! :)

Was this page helpful?
0 / 5 - 0 ratings