F#中记录数组到DataTable的通用转换,反之亦然

yos*_*ian 4 datatable f# datagridview

我有一个简单的F#以下类型的记录数组(或依赖于系统类型的一些小变化):

type Transaction= {
    date: DateTime;
    amount: float;
    mutable details: string }
Run Code Online (Sandbox Code Playgroud)

我想将它们显示到Winforms中的DataGridView控件,包括支持更改内容,添加或删除行/记录.将DataViewGrid绑定到记录数组似乎不允许添加/删除行(或者我必须做错了吗?).但是,DataTable结构似乎更适合此目的.创建上述类型的DataTable似乎与原始记录类型完全匹配,例如:

let dataTable = new DataTable()
    dataTable.Columns.Add("date", typeof<DateTime>) |> ignore
    dataTable.Columns.Add("amount", typeof<float>) |> ignore
    dataTable.Columns.Add("details", typeof<string>) |> ignore)
Run Code Online (Sandbox Code Playgroud)

所以我想知道是否有一个现有的系统函数将依赖于系统类型的任何类型的记录转换为记录数组(例如,键入"事务数组")到相应的DataTable中,反之亦然?如果没有这样的功能,怎么能以简洁的方式完成呢?

Tom*_*cek 9

没有内置功能.

实现转换的最直接方法是使用F#反射,但是天真地使用反射(如此)可能会非常慢,因此您应该测量性能是否足以满足您的要求(如果是关闭在UI中显示内容的转换,这可能很好).

这是一个如何做到的草图:

open Microsoft.FSharp.Reflection

let recordsToDataTable (items:'T list) =
  // Create data table and add fields based on the record type
  let dataTable = new DataTable() 
  let fields = FSharpType.GetRecordFields(typeof<'T>)
  for fld in fields do
    dataTable.Columns.Add(fld.Name, fld.PropertyType) |> ignore
  // Add values from the specified list to the table
  for itm in items do
    let row = dataTable.NewRow()
    // Set all fields of the current row
    for fld in fields do
      row.[fld.Name] <- fld.GetValue(itm)
    dataTable.Rows.Add(row)
  dataTable      

let dataTableToRecords (table:DataTable) : 'T list =
  let fields = FSharpType.GetRecordFields(typeof<'T>)
  // Create a list with item for every row in the table
  [ for row in table.Rows ->
      // Get values of all fields from DT and create a record value
      let values = [| for fld in fields -> row.[fld.Name] |]
      FSharpValue.MakeRecord(typeof<'T>, values) :?> 'T ]
Run Code Online (Sandbox Code Playgroud)