Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled Exception: ... invalid name ... at LiteDB.BsonDocument.set_Item in 'mutable' field #27

Closed
srlopez opened this issue May 4, 2019 · 7 comments

Comments

@srlopez
Copy link

srlopez commented May 4, 2019

The mutable attribute avoids insert in LiteDB, or passing through Json's encoder, something happens that the name of the field is modified.

open System
open LiteDB
open LiteDB.FSharp

[<StructuredFormatDisplay("a{SizeGb}GB")>]
[<CLIMutable>]
type Disk = 
    { SizeGb : int }

[<StructuredFormatDisplay("Computer #{Id}: {Manufacturer}/{DiskCount}/{Disks}")>]
[<CLIMutable>]
type Computer =
    { Id: int
      mutable Manufacturer: string
      mutable Disks: Disk list }
    [<BsonIgnore>] 
    member __.DiskCount = __.Disks.Length  // property calculated

[<EntryPoint>]
let main argv =

    let myPc =
        { Id = 0
          Manufacturer = "Computers Inc."
          Disks =
            [ { SizeGb = 100 }
              { SizeGb = 250 }
              { SizeGb = 500 } ] }
    printfn "%A" myPc

    let mapper = FSharpBsonMapper()
    use db = new LiteDatabase("data.db", mapper)
    let computers = db.GetCollection<Computer>("computers")

    // All right , but if I remove the comment on next line I get the error
    // computers.Insert(myPc) |> ignore
    myPc.Manufacturer <- "Dell"
    printfn "%A" myPc
    ...

The field you are trying to modify Manufacturer seems to change its name Manufacturer@'
The full error is:

Unhandled Exception: System.ArgumentException: Field 'Manufacturer@' has an invalid name.
   at LiteDB.BsonDocument.set_Item(String name, BsonValue value)
   at LiteDB.JsonReader.ReadObject()
   at LiteDB.JsonReader.ReadValue(JsonToken token)
   at LiteDB.JsonSerializer.Deserialize(String json)
   at LiteDB.FSharp.Bson.serialize[t](t entity)
   at LiteDB.FSharp.FSharpBsonMapper.ToDocument[t](t entity)
   at LiteDB.LiteCollection`1.Insert(T document)
   at Program.main(String[] argv) in /app/Program.fs:line ..

Thanks

@srlopez srlopez changed the title Unhandled Exception: ... invalid name ... at LiteDB.BsonDocument.set_Item Unhandled Exception: ... invalid name ... at LiteDB.BsonDocument.set_Item in mutable field May 4, 2019
@srlopez srlopez changed the title Unhandled Exception: ... invalid name ... at LiteDB.BsonDocument.set_Item in mutable field Unhandled Exception: ... invalid name ... at LiteDB.BsonDocument.set_Item in 'mutable' field May 4, 2019
@humhei
Copy link
Collaborator

humhei commented May 4, 2019

mutable Manufacturer
dnspy: Decompiled c# codes

public string Manufacturer@;

Converter will try to serialize both property Manufacturer and field Manufacturer@

immutable Manufacturer
dnspy: Decompiled c# codes

internal string Manufacturer@;

Converter will only try to serialize property Manufacturer

@srlopez
Copy link
Author

srlopez commented May 4, 2019

@humhei
How can I fix it? It's a problem of my code or it's an authentic issue?

@humhei
Copy link
Collaborator

humhei commented May 4, 2019

In fsharp world
mutable fields in record type is not recommanded(Not thread safe; I didn't see others to use it)
What's your use case of it?

workaround

Create a new record instead of setting property mutable

open System
open LiteDB
open LiteDB.FSharp

[<StructuredFormatDisplay("a{SizeGb}GB")>]
[<CLIMutable>]
type Disk = 
    { SizeGb : int }

[<StructuredFormatDisplay("Computer #{Id}: {Manufacturer}/{DiskCount}/{Disks}")>]
[<CLIMutable>]
type Computer =
    { Id: int
      Manufacturer: string
      Disks: Disk list }
    [<BsonIgnore>] 
    member __.DiskCount = __.Disks.Length  // property calculated

[<EntryPoint>]
let main argv =

    let myPc =
        { Id = 0
          Manufacturer = "Computers Inc."
          Disks =
            [ { SizeGb = 100 }
              { SizeGb = 250 }
              { SizeGb = 500 } ] }
    printfn "%A" myPc

    let mapper = FSharpBsonMapper()
    use db = new LiteDatabase("data.db", mapper)
    let computers = db.GetCollection<Computer>("computers")

    let myDellPc = 
            {myPc with Manufacturer = "Dell" }
    computers.Insert(myDellPc) |> ignore
    
...

@Zaid-Ajaj
Copy link
Owner

Hmm even though it is not recommended, I don't see an issue in supporting mutable constructs in the library, I will work on it...

@srlopez
Copy link
Author

srlopez commented May 4, 2019

Hmm even though it is not recommended, I don't see an issue in supporting mutable constructs in the library, I will work on it...

And [<BsonIgnore>] and [<BsonField>] too, please

@Zaid-Ajaj
Copy link
Owner

I am afraid that [<BsonIgnore>] doesn't really make sense within F# records. Using a member within the record is the way to go as it will be used to compute properties derived from the record fields and doesn't get persisted (= ignored) as follows:

[<CLIMutable>]
type Computer =
    { Id: int
      Manufacturer: string
      Disks: Disk list }

    // this is not persisted
    with member this.Ignored = sprintf "%d => %s" this.Id this.Manufacturer

Aren't these members good enough for your use-case?

we can continue discussion at #26

As for [<BsonField>] I think it is possible but will require more work, I will open another issue for it.

@Zaid-Ajaj
Copy link
Owner

Fixed and released in version 2.8.0 to nuget, should be available really soon when nuget does it's magic. Now I decided to take control of how the fields are marshalled, so now these mutable records will work just fine 😄

humhei added a commit to humhei/LiteDB.FSharp that referenced this issue May 10, 2019
* Introduce auto-quoted query expressions, publish 2.7.0

* Support mutable values -> fixes Zaid-Ajaj#27. Take back control of record marshalling

* Add tests for ignored members just to be sure, see Zaid-Ajaj#26

* Add support for literal boolean values in query expressions

* Support comparison operators for id fields by @humhei at Zaid-Ajaj#31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants