Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
238 views
in Technique[技术] by (71.8m points)

sqlite - Converting data objects - F#, Fabulous, sql-net-pcl and a web API client library

I'm very new to F# and trying to figure out the best way to convert between similar but different data objects while minimising duplication of code. I'm writing an app using the Fabulous F# MVU framework for Xamarin Forms.

I'm using sqlite-net-pcl for data persistence, which calls for an object when creating a database table and interacting with that table, and I have created an AuthorObject:

    type AuthorObject() =
        [<PrimaryKey>] // sqlite-net-pcl attribute
        member val AuthorId = 0 with get, set
        member val Username = "" with get, set
        member val Token = "" with get, set

Fabulous makes use of record types for models and I have created an Author record type to pass around inside Fabulous:

    type Author = 
        { AuthorId: int
          Username: string
          Token: string }

But I also have to interact with a web service API library (written in C#) that returns an AuthorDetailDto object. AuthorDetailDto has the same named properties on it as above, plus more that I don't need to worry about. I cannot change the definition of AuthorDetailDto.

So it seems the constraints are currently that sqlite-net-pcl needs an object with members, Fabulous needs a record type, and I have this AuthorDetailDto object coming across from the service API client library that I can't change. I need to convert between these types and I initially was hoping that I could get away with just:

    let convertToObject author = // Was hoping author param here could be generic in some way
        let obj = AuthorObject()
        obj.AuthorId <- author.AuthorId
        obj.Username <- author.Username
        obj.Token <- author.Token
        obj

    let convertToModel (obj: AuthorObject) : Author =
        { AuthorId = obj.AuthorId
          Username = obj.Username
          Token = obj.Token }

But with the added complication of AuthorDetailDto, I'm finding I now need to do something like this to keep the compiler happy:

    let convertAuthorDetailDtoToObject (dto: AuthorDetailDto) =
        let obj = AuthorObject()
        obj.AuthorId <- dto.AuthorId
        obj.Username <- dto.Username
        obj.Token <- dto.Token
        obj

    let convertAuthorToObject (author: Author) =
        let obj = AuthorObject()
        obj.AuthorId <- author.AuthorId
        obj.Username <- author.Username
        obj.Token <- author.Token
        obj

    let convertToModel (obj: AuthorObject) : Author =
        { AuthorId = obj.AuthorId
          Username = obj.Username
          Token = obj.Token }

Here are some source files:

DomainModels.fs:

namespace MyApp.Mobile

module DomainModels =
    type Author = 
        { AuthorId: int
          Username: string
          Token: string }

Author.fs:

namespace MyApp.Mobile.Repository

open SQLite
open Client // Provides AuthorDetailDto.
open MyApp.Mobile.DomainModels

module Author =
    type AuthorObject() =
        [<PrimaryKey>]
        member val AuthorId = 0 with get, set
        member val Username = "" with get, set
        member val Token = "" with get, set

    let convertToObject author =
        let obj = AuthorObject()
        obj.AuthorId <- author.AuthorId
        obj.Username <- author.Username
        obj.Token <- author.Token
        obj

    let convertToModel (obj: AuthorObject) : Author =
        { AuthorId = obj.AuthorId
          Username = obj.Username
          Token = obj.Token }

    let connect dbPath = async {
            let db = SQLiteAsyncConnection(SQLiteConnectionString dbPath)
            do! db.CreateTableAsync<AuthorObject>() |> Async.AwaitTask |> Async.Ignore
            return db
    }

    let purgeLoggedInAuthor dbPath = async {
        let! db = connect dbPath
        do! db.ExecuteAsync "DELETE FROM AuthorObject" |> Async.AwaitTask |> Async.Ignore
    }

    let insertLoggedInAuthor dbPath author = async {
        let! db = connect dbPath
        do! db.InsertAsync (convertToObject author) |> Async.AwaitTask |> Async.Ignore
    }

The initial call looks like this:

insertLoggedInAuthor dbPath loggedInAuthor // loggedInAuthor is an AuthorDetailDto.

The error is FS0001 This expression was expected to have type 'DomainModels.Author' but here has type 'AuthorDetailDto' due to both types being referenced in Author.fs.

I've tried messing around with inline and static member constraints, and attempting to define an interface, but due to Author needing to be a record type and not being able to change AuthorDetailDto, it doesn't seem like this approach is viable. Or perhaps I'm just not well versed enough in F#.

To those more familiar with F# than me, what is the best way to address this kind of situation to minimise code duplication?

question from:https://stackoverflow.com/questions/65894470/converting-data-objects-f-fabulous-sql-net-pcl-and-a-web-api-client-library

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)
Waitting for answers

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...