Преобразование объектов данных - F#, Fabulous, sql-net-pcl и клиентская библиотека веб-API
Я новичок в F# и пытаюсь найти лучший способ преобразования между похожими, но разными объектами данных, минимизируя дублирование кода. Я пишу приложение, используя платформу Fabulous F# MVU для Xamarin Forms.
Я использую 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 использует типы записей для моделей, и я создал тип записи, который будет передаваться внутри Fabulous:
type Author =
{ AuthorId: int
Username: string
Token: string }
Но мне также нужно взаимодействовать с библиотекой API веб-службы (написанной на C#), которая возвращает объект. имеет свойства с такими же именами, как указано выше, а также многое другое, о чем мне не нужно беспокоиться. Я не могу изменить определение.
Таким образом, похоже, что в настоящее время ограничения заключаются в том, что sqlite-net-pcl нужен объект с членами, Fabulous нужен тип записи, и у меня есть этот объект, поступающий из клиентской библиотеки 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 }
Но с дополнительным усложнением, я обнаружил, что теперь мне нужно сделать что-то вроде этого, чтобы компилятор был доволен:
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#, чем я, как лучше всего решить такую ситуацию, чтобы минимизировать дублирование кода?