mbdavid / LiteDB

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

ArgumentException when fetching db item with missing Enum value #2514

Open ionite34 opened 4 days ago

ionite34 commented 4 days ago

Version LiteDB version: 5.0.19 .NET: 8.0 OS: Occurs across our users in Win 11 / macOS / Linux

Describe the bug ArgumentException occurs when fetching litedb entries with properties of enums that have names that no longer exist or were changed. Is there a way for the bson deserializer to return 0 or some default enum value when this happens?

Code to Reproduce

  1. First saving value:
    
    using LiteDB;

var tempDir = Path.Combine(Path.GetTempPath(), "litedb-test-e31c667160d8"); Directory.CreateDirectory(tempDir);

using var db = new LiteDatabase(Path.Combine(tempDir, "test.db"));

var collection = db.GetCollection("Cat");

collection.Upsert(new Cat { Id = 100, Color = Color.Green });

public enum Color { Red = 0, Green = 1, Blue = 2 }

public class Cat { public int Id { get; set; } public Color Color { get; set; } }


2. Finding item with missing enum value:
```cs
using LiteDB;

var tempDir = Path.Combine(Path.GetTempPath(), "litedb-test-e31c667160d8");
Directory.CreateDirectory(tempDir);

using var db = new LiteDatabase(Path.Combine(tempDir, "test.db"));

var collection = db.GetCollection<Cat>("Cat");

var cat = collection.FindById(100); // Error here
Console.WriteLine(cat?.Color);

public enum Color
{
    Red = 0,
    // Green = 1,
    Blue = 2
}

public class Cat
{
    public int Id { get; set; }
    public Color Color { get; set; }
}

Exception:

System.ArgumentException: Requested value 'Green' was not found.
   at System.Enum.TryParseByName[TStorage](RuntimeType enumType, ReadOnlySpan`1 value, Boolean ignoreCase, Boolean throwOnFailure, TStorage& result)
   at System.Enum.TryParseByValueOrName[TUnderlying,TStorage](RuntimeType enumType, ReadOnlySpan`1 value, Boolean ignoreCase, Boolean throwOnFailure, TUnderlying& result)
   at System.Enum.TryParse(Type enumType, ReadOnlySpan`1 value, Boolean ignoreCase, Boolean throwOnFailure, Object& result)
   at System.Enum.Parse(Type enumType, String value, Boolean ignoreCase)
   at LiteDB.BsonMapper.Deserialize(Type type, BsonValue value)
   at LiteDB.BsonMapper.DeserializeObject(EntityMapper entity, Object obj, BsonDocument value)
   at LiteDB.BsonMapper.Deserialize(Type type, BsonValue value)
   at LiteDB.LiteQueryable`1.<ToEnumerable>b__27_2(BsonDocument x)
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at LiteDB.LiteCollection`1.FindById(BsonValue id)
   ...
JKamsker commented 4 days ago

What, in your opinion, would be the expected result? As far as I can see, LiteDB serializes Enums as string and when you are trying to parse the string into an enum value, that doesnt exist, it fails.

ionite34 commented 4 days ago

What, in your opinion, would be the expected result? As far as I can see, LiteDB serializes Enums as string and when you are trying to parse the string into an enum value, that doesnt exist, it fails.

I agree the error by default seems to be standard since I think behavior in System.Text.Json is the same. I'm wondering if a config option for UnknownEnumAsDefault like EnumAsInteger for BsonMapper is available to deal with this globally? Or can I implement that myself with BsonMapper.Global.RegisterType ?