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

时间:2012-10-05 07:47:32

标签: datagridview datatable f#

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

type Transaction= {
    date: DateTime;
    amount: float;
    mutable details: string }

我想将它们显示到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)

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

1 个答案:

答案 0 :(得分:9)

没有内置功能。<​​/ p>

实现转换的最直接方法是使用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 ]