mbdavid / LiteDB

LiteDB - A .NET NoSQL Document Store in a single data file
http://www.litedb.org
MIT License
8.53k stars 1.24k forks source link

StackOverflowException #927

Open Almis90 opened 6 years ago

Almis90 commented 6 years ago

My models looks like this

public class User
{

        [BsonId]
        public string Id { get; set; }
        public UserType Type { get; set; }
        public string Name{ get; set; }
        public DateTime CreatedAt { get; }

        [BsonRef]
        public Device Device { get; set; }
}
public class Device
{
        [BsonId]
        public string Id { get; set; }
        public string Name { get; set; }
        public bool IsEnabled { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime UpdatedAt { get; set; }

        [BsonRef]
        public List<User> Users { get; set; }
}

I know this is wrong because I getting the below exception but what is the right way to do that?

 LiteDB.dll!LiteDB.Reflection.CreateGenericGetter(System.Type type, System.Reflection.MemberInfo memberInfo) Line 38    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.BuildEntityMapper(System.Type type) Line 277   C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.GetEntityMapper(System.Type type) Line 221 C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRefList(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 431    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRef(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 379    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.BuildEntityMapper(System.Type type) Line 309   C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.GetEntityMapper(System.Type type) Line 221 C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRefItem(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 393    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRef(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 383    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.BuildEntityMapper(System.Type type) Line 309   C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.GetEntityMapper(System.Type type) Line 221 C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRefList(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 431    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRef(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 379    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.BuildEntityMapper(System.Type type) Line 309   C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.GetEntityMapper(System.Type type) Line 221 C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRefItem(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 393    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.RegisterDbRef(LiteDB.BsonMapper mapper, LiteDB.MemberMapper member, string collection) Line 383    C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.BuildEntityMapper(System.Type type) Line 309   C#  Symbols loaded.
    LiteDB.dll!LiteDB.BsonMapper.GetEntityMapper(System.Type type) Line 221 C#  Symbols loaded.
    The maximum number of stack frames supported by Visual Studio has been exceeded.        Annotated Frame
PathogenDavid commented 4 years ago

This is a fairly old issue, but I ran into it and looked into it a bit.

This is caused by recursive/mutually recursive references. BsonMapper.RegisterDbRefItem and RegisterDbRefList rely on getting the entity mapper for the referenced type. This is done when the field is registered so that we can avoid looking up the entity mapper each time the field is de/serialized. Unfortunately, LiteDb attempts to build a new mapper whenever it encounters a type that isn't mapped yet.

Also it's worth noting this is still an issue in v5.


The easiest workaround is to just store the ID manually and resolve the reference yourself on an as-needed basis.


If you're willing to modify LiteDB (or make your own BsonMapper) and don't mind the performance hit, I very briefly tested moving the entity mapper lookup into the de/serialization methods for reference fields. (Diff here)

This works, but it is not particularly efficient. If you really want this, I think a better strategy would be to cache the mapper on the first de/serialization.

Hidend commented 3 years ago

I'm having the same problem, and the answer above is right, it does happen when referencing with BsonRef mutually two classes and one is not mapped yet, @mbdavid @lbnascimento