Если вы хотите представить проблемы с данными или параметрами запроса, например, не найти конкретную запись, отличную от других типов сбоев, например, не подключиться к базе данных или выполнить необработанное исключение, вы можете создать дискриминационный союз для явного представления ваших ожидаемых проблем с данными / запросами. Затем вы можете вернуть этот DU вместо string
в качестве данных для случая Error
, и вам действительно не понадобится случай Failure
. Рассмотрим что-то вроде этого:
type SqlError =
| NoMatchingRecordsFound of SqlParameter list
| CouldNotConnectToDatabase
| UserDoesNotHaveQueryPermissions
| UnhandledException of exn
Я бы посоветовал взглянуть на Железнодорожно-ориентированное программирование и следовать шаблону определения дискриминированного объединения для различных случаев ошибок. Вы все еще можете включить сообщение об ошибке в этот DU, но я бы предложил использовать явные случаи для всех ваших различных ожидаемых отказов, а затем иметь «UnhandledException» или аналогичный случай для ваших непредвиденных ошибок, возможно, с exn
для данных.
Если вам интересно, у меня есть библиотека на GitHub / NuGet , которая объединяет все элементы ROP и добавляет совместимость с Task
, Async
, и Lazy
печатает в одном компоновщике вычислений, поэтому вам не нужно включать весь этот код в ваш собственный проект.
EDIT
Вот полный пример выполнения этого в стиле ROP (с использованием связанной структуры):
open FSharp.Control
open System.Linq
// Simulate user table from type-provider
[<AllowNullLiteral>]
type User() =
member val Id = 0 with get,set
member val Name = "" with get,set
// Simulate a SQL DB
let users = [User(Id = 42, Name = "The User")].AsQueryable()
// Our possible events from the SQL query
type SqlEvent =
| FoundUser
| UserIdDoesNotExist of int
| CouldNotConnectToDatabase
| UnhandledException of exn
// Railway-Oriented function to find the user by id or return the correct error event(s)
let getUserById id =
operation {
let user =
query {
for user in users do
where (user.Id = id)
select user
headOrDefault
}
return!
if user |> isNull
then Result.failure [UserIdDoesNotExist id]
else Result.successWithEvents user [FoundUser]
}
Вызов getUserById 42
вернет успешно завершенную операцию с пользователем и событие FoundUser
. Вызов getUserById
для любого другого номера (например, 0) вернет неудачную операцию с событием ошибки UserIdDoesNotExist 0
. При необходимости вы добавите больше событий.