F# 如何将数据库记录读入受歧视的联合?

F# 如何将数据库记录读入受歧视的联合?,f#,discriminated-union,F#,Discriminated Union,假设我在F#中有以下内容: 其次是受歧视的工会: type Visit = | Scheduled of name: PersonName * appointmentTime: DateTime | WalkIn of name: PersonName * appointmentTime: DateTime * timeOfService: DateTime | KeptAppointment of name: PersonName * appointmentTime: DateTime *

假设我在F#中有以下内容:

其次是受歧视的工会:

type Visit = 
| Scheduled of name: PersonName * appointmentTime: DateTime
| WalkIn of name: PersonName * appointmentTime: DateTime * timeOfService: DateTime
| KeptAppointment of name: PersonName * appointmentTime: DateTime * postingTime: DateTime * serviceTime: DateTime
| OpenSlot of appointmentTime: DateTime

with 
  member x.Name = 
      match x with
      | Scheduled(name, _)
      | WalkIn(name, _, _)
      | KeptAppointment(name, _, _, _) -> Some name
      | OpenSlot _ -> None
如果访问定义为简单记录,如:

type Visit = {
    lastName : string
    firstName : string
    birthDate : DateTime
    appointmentTime : Nullable<DateTime>
    tservice : Nullable<DateTime>
    postingTime : Nullable<DateTime>
    chartNumber : Nullable<int>
    }
但是,如果使用了Visit的第一个版本(歧视联盟),则“编译器”无法识别如何处理人名的生日、名和姓。显然,我没有正确地传达我的意图


那么,如何将数据库记录读入受歧视的联合?

有许多不同的方法可以做到这一点;所有这些都与大多数ORM处理继承类的方式非常相似。然而,考虑到目前数据库的状况,还有其他方法

从根本上说,所有的联合都是由给定的“标记”分派的一组类型。对于每个标记/联合案例,都有一组必须定义的字段(记录)。因此,您需要在映射lambda中找到一种方法来检查某种条件,并适当地创建正确的联合类型

let GetScheduleAsync (tableDate : DateTime) =
        async {
            let! data = context.GetOfficeScheduleAsync(tableDate) |> Async.AwaitTask
            return data |> Seq.map(fun q -> 
                match q.Tag with
                | x when x = "Scheduled" -> Scheduled({ firstName = q.firstName; lastName = q.lastName; birthDate = q.BirthDate }, q.appointmentTime)
                | _ -> failwithf "Insert other cases here"
                )                  
          }
          |> Async.StartAsTask

如何确定“标记”实际上是数据库设计的一个问题,有许多不同的策略(例如,带有标记字段的单个表、每个具体类/标记的表)。一些数据库还允许JSON序列化(例如Postgres),如果您只想将union直接序列化/反序列化到字段中,可以选择JSON序列化。如何在数据库中表示数据以及如何将数据读入union构造函数完全取决于您。

为此,我将为数据库IO创建一个层。DU数据的实体记录,只有基本类型,在需要时可为空,DU案例有一个字段。@BentTranberg请解释“DU案例有一个字段”是如何工作的?谢谢。是否可以直接使用上面“member x.Name”定义的模式匹配,而不添加与“when”相关的专门测试?谢谢。“让(|等于| | |)xy=如果x=y,那么其他一些就没有了”。然后你可以在任何你想要的地方使用它来进行对等匹配。“将x.名称与|等于(“x”)->…”匹配”。我不记得这是否有内在的匹配;常量模式可能会起作用,但如果我记得的话,它对字符串不起作用。因为如果您定义了枚举,那么您事先就知道这些情况,所以最好先尝试解析它,然后匹配枚举。这样,您就可以更改它在数据库(x.Name)中的表示方式,而无需稍后更改上述逻辑。通常,标记是类似Int的东西,而不是字符串,可以用这种方式在数据库中表示。
/// Get the office schedule for the tableDate.
    let GetScheduleAsync (tableDate : DateTime) =
        async {
            let! data = context.GetOfficeScheduleAsync(tableDate) |> Async.AwaitTask
            return data |> Seq.map(fun q -> {
                Visit.lastName = q.lastname
                firstName = q.firstname
                birthDate = q.birthdate
                appointmentTime = q.appointment_time
                tservice = q.service_time
                postingTime = q.posting_time
                chartNumber = q.chart_number
                })                  
          }
          |> Async.StartAsTask
let GetScheduleAsync (tableDate : DateTime) =
        async {
            let! data = context.GetOfficeScheduleAsync(tableDate) |> Async.AwaitTask
            return data |> Seq.map(fun q -> 
                match q.Tag with
                | x when x = "Scheduled" -> Scheduled({ firstName = q.firstName; lastName = q.lastName; birthDate = q.BirthDate }, q.appointmentTime)
                | _ -> failwithf "Insert other cases here"
                )                  
          }
          |> Async.StartAsTask