mbdavid / LiteDB

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

[BUG] Cannot serialize Record (C# 9) #2143

Open roubachof opened 2 years ago

roubachof commented 2 years ago

Version 5.0.11 / Xamarin.Forms Android

Describe the bug

LiteDB.LiteException: Failed to create instance for type 'CovaSud.TeamCollect.Domain.Tours.TourInfo' from assembly

ERROR|1|App|Error in wrapped task --> LiteDB.LiteException: Failed to create instance for type 'CovaSud.TeamCollect.Domain.Tours.TourInfo' from assembly 'CovaSud.TeamCollect.Domain.Tours.TourInfo, TeamCollect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. Checks if the class has a public constructor with no parameters. ---> System.ArgumentException: Type 'CovaSud.TeamCollect.Domain.Tours.TourInfo' does not have a default constructor
Parameter name: type
  at System.Linq.Expressions.Expression.New (System.Type type) [0x00070] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Linq.Expressions/src/System/Linq/Expressions/NewExpression.cs:196 
  at LiteDB.Reflection.CreateClass (System.Type type) [0x00015] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
  at LiteDB.Reflection.CreateInstance (System.Type type) [0x00066] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
   --- End of inner exception stack trace ---
  at LiteDB.Reflection.CreateInstance (System.Type type) [0x0018f] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
  at LiteDB.BsonMapper+<>c__DisplayClass59_0.<Deserialize>b__0 (LiteDB.BsonDocument v) [0x0000b] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
  at LiteDB.BsonMapper.Deserialize (System.Type type, LiteDB.BsonValue value) [0x00246] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
  at LiteDB.LiteQueryable`1[T].<ToEnumerable>b__27_2 (LiteDB.BsonDocument x) [0x00000] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
  at System.Linq.Enumerable+SelectEnumerableIterator`2[TSource,TResult].MoveNext () [0x00036] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Linq/src/System/Linq/Select.cs:131 
  at System.Linq.Enumerable.TryGetFirst[TSource] (System.Collections.Generic.IEnumerable`1[T] source, System.Boolean& found) [0x00045] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Linq/src/System/Linq/First.cs:63 
  at System.Linq.Enumerable.FirstOrDefault[TSource] (System.Collections.Generic.IEnumerable`1[T] source) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Linq/src/System/Linq/First.cs:34 
  at LiteDB.LiteQueryable`1[T].FirstOrDefault () [0x00006] in <47be80e4df11402fa4b0c9d0d61b2517>:0 
  at CovaSud.TeamCollect.Domain.Tours.Impl.TourLocalRepository+<>c__DisplayClass6_0.<TryGetTour>b__0 () [0x00038] in D:\Dev\CovaSud\TeamCollect\TeamCollect\Domain\Tours\Impl\TourLocalRepository.cs:32 
  at CovaSud.TeamCollect.Domain.Tours.Impl.TourService.GetTour (System.Int32 tourId, System.DateTime tourDate, System.Boolean refresh) [0x0009f] in D:\Dev\CovaSud\TeamCollect\TeamCollect\Domain\Tours\Impl\TourService.cs:41 
  at CovaSud.TeamCollect.Presentation.Pages.Collections.Tours.TourPageViewModel.LoadTour (System.Boolean refresh) [0x0006f] in D:\Dev\CovaSud\TeamCollect\TeamCollect\Presentation\Pages\Collections\Tours\TourPageViewModel.cs:116 
  at Sharpnado.Tasks.TaskMonitorBase.MonitorTaskAsync () [0x0011e] in <9c7e2c92bb6e40e591c251583aa12e08>:0 

Code to Reproduce

Try to serialize a simple Record object:

public record TourInfo(
        int Id,
        Sector? Sector,
        Collector Collector,
        TourState TourState,
        string? Name,
        DateTime Date,
        List<int> SortedCollectionIdList)
    {
    }
e2ibrobbins commented 2 years ago

I have a similar issue. In my case, I am working in F# (.NET 6.0) and using v.5.0.11 of the library. I am using a record object. It seems to get added to the database collection, but finding it again produces the reported error.

broknecho commented 2 years ago

@roubachof Try doing the little bit more verbose version with init setters so that a default parameter-less constructor is created:

public record TourInfo()
{
    public int Id { get; init; }
    public Sector? Sector { get; init; }
    public Collector Collector { get; init; }
    public TourState TourState { get; init; }
    public string? Name { get; init; }
    public DateTime Date { get; init; }
    public List<int> SortedCollectionIdList { get; init; }
}

See if that works for you. This still gives the benefit of the immutable structure but is a little longer.

roubachof commented 2 years ago

hey @broknecho ! We changed our database since records and immutability is the base of our architecture, I'm afraid I won't be able to test this workaround (and I guess we wouldn't consider it acceptable :)

Lonli-Lokli commented 2 years ago

@roubachof But that's init-only setter, ie setters available only on costructor level - same code as yours, just with another syntax

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/init#motivation

Argh, I see - it does not require them to set, just alllow

roubachof commented 2 years ago

I know it's only a syntax issue, but the construction declaration syntax is so much concise and readable I won't trade it vs something as verbose as init declaration, which is just, awful...

aliegeni commented 1 year ago

Issue seems to happen only with reference type positional parameters of a record type. Only workaround I found is refactor its to required init properties:

public record TourInfo(int Id, string Name)
{
    public required Sector Sector { get; init; }
}
Narvalex commented 1 year ago

In my case it fails with the nullable values...