2

我对 F# 非常陌生,并试图找出在相似但不同的数据对象之间进行转换的最佳方法,同时最大限度地减少代码重复。我正在使用适用于 Xamarin Forms 的 Fabulous F# MVU 框架编写应用程序。

我正在使用 sqlite-net-pcl 进行数据持久性,它在创建数据库表并与该表交互时调用一个对象,并且我创建了一个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 使用模型的记录类型,我创建了一个Author记录类型以在 Fabulous 内部传递:

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

但我还必须与返回AuthorDetailDto对象的 Web 服务 API 库(用 C# 编写)进行交互。AuthorDetailDto上面有与上面相同的命名属性,还有更多我不需要担心的。我无法更改 的定义AuthorDetailDto

所以目前的限制似乎是 sqlite-net-pcl 需要一个带有成员的对象,Fabulous 需要一个记录类型,而且我有这个AuthorDetailDto对象来自我无法更改的服务 API 客户端库。我需要在这些类型之间进行转换,我最初希望我能侥幸逃脱:

    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 }

但是由于增加了复杂性AuthorDetailDto,我发现我现在需要做这样的事情来让编译器满意:

    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 }

以下是一些源文件:

DomainModels.fs:

namespace MyApp.Mobile

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

作者.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
    }

初始调用如下所示:

insertLoggedInAuthor dbPath loggedInAuthor // loggedInAuthor is an AuthorDetailDto.

该错误是FS0001 This expression was expected to have type 'DomainModels.Author' but here has type 'AuthorDetailDto'由于在 Author.fs 中引用了这两种类型。

我尝试过使用inline静态成员约束,并尝试定义接口,但由于Author需要成为记录类型并且无法更改AuthorDetailDto,因此这种方法似乎不可行。或者我只是对 F# 不够精通。

对于那些比我更熟悉 F# 的人,解决这种情况以尽量减少代码重复的最佳方法是什么?

4

0 回答 0