Zaid-Ajaj / LiteDB.FSharp

Advanced F# Support for LiteDB, an embedded NoSql database for .NET with type-safe query expression through F# quotations
MIT License
180 stars 22 forks source link

Plans for [<BsonIgnore>] #26

Closed srlopez closed 5 years ago

srlopez commented 5 years ago

Hello, as you can see in the F# forum. Studying how to introduce my students in F#, we were converting a small java project to F#, and to avoid philosophical discussions about SQL and ORM, LiteDB is the perfect solution. But the attributes calculated according to other attributes are also saved in LiteDB, we have used [<BsonIgnore>] (on BsonMapper.cs Line 252,) but we have already seen that there is no account. You have plans to implement it. And what about [<BsonField>]?

Zaid-Ajaj commented 5 years ago

Hello @srlopez, I finally got around and made allocated some time to work on these issues. Ok, first of all the BsonIgnore attributes isn't taken into account when persisting documents from records. This choice is by design as record fields in F# should not depend on each other, instead you should use calculated using either members or standalone functions:

type Disk = { SizeGb : int }

type Computer =
    { Id: int
      Manufacturer: string
      Disks: Disk list }

    with member this.Ignored = sprintf "%d => %s" this.Id this.Manufacturer

here you see the member Ignored will be persisted inside the database.

This is an interesting case of separating data and behavior from each other. If you want to introduce F# to your students, it is better to showcase best practices with idiomatic F# code: no mutation, only data transformations. here is a full example:

// Learn more about F# at http://fsharp.org

open LiteDB 
open LiteDB.FSharp 
open System.IO

[<CLIMutable>]
type Disk = 
    { SizeGb : int }

[<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

[<EntryPoint>]
let main argv =

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

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

    // Insert & Print
    computers.Insert(myPc) |> ignore
    let insertedPc = computers.FindAll() |> Seq.head
    printfn "%A, %s" insertedPc insertedPc.Ignored
    0

Notice I am using InMemory database which is better when testing so that you won't get different versions of your document inside the databse

srlopez commented 5 years ago

You are right. ... it is better to showcase best practices with idiomatic F# code: no mutation, only data transformations. Good aproach to 'Ignore' members. But my opinion, that sure that is incorrect since I am also a rookie with F#, is that as that attribute was in the original in C#, a wrapper should cover it, regardless of whether it is idiomatic or not in the language in which the wrapper is implemented. Anyway thank you @Zaid-Ajaj very much, I have learned a lot from your implementation

srlopez commented 5 years ago

Without InMemory, and persisting data to use db = new LiteDatabase("data.db", mapper). Running LiteDB.Shell like this:

dotnet LiteDB.Shell.dll data.db --run data.cmd
Welcome to LiteDB Shell

Getting started with `help`, `help full` or `help <command>`

> open data.db
> run data.cmd
> show collections
computers
> db.computers.find
[1]: {"_id":1,"Manufacturer":"Computers Inc.","Disks":[{"SizeGb":100},{"SizeGb":250},{"SizeGb":500}],"Ignored":"0 => Computers Inc."}
[2]: {"_id":2,"Manufacturer":"Computers Inc.","Disks":[{"SizeGb":100},{"SizeGb":250},{"SizeGb":500}],"Ignored":"0 => Computers Inc."}
> exit

As you can see, member Ignored is persisted, with the ID original in code 0. Some thing is not working to me. Thanks

Zaid-Ajaj commented 5 years ago

is that as that attribute was in the original in C#, a wrapper should cover it, regardless of whether it is idiomatic or not in the language in which the wrapper is implemented.

I build a lot of these "wrappers" and usually I try to take just the good parts that make sense to use within F#

Some thing is not working to me.

Have you updated your nuget of LiteDB.FSharp from 2.7.0 to 2.8.0? I refactored the way records are persisted, only fields are persisted

srlopez commented 5 years ago

OMG! that's was the problem,

dotnet remove package LiteDB.FSharp
dotnet add package LiteDB.FSharp --version 2.8.0

and now Ignored is ignored. Thanks

Zaid-Ajaj commented 5 years ago

Glad to hear that it is working! :smile:

I will consider this issue as resolved, if you encounter any problem or need any help, just let know!

Cheers