Before some years I had created a library in .NET 1.1 for utility functions regarding binding DataTable values to ASP.NET web controls. Two of those were about populating and persisting values from connection (many-to-many) Tables. There was an idea to port this library to F#.
In Notrhwind Db there are three tables Employees, EmployeeTerritories and Territories.

  

   Imagine that in the web form of Employee, there is a ListBox that contains all the Territories and there is a need to select all appropriate Territories for that Employee. Then persist the selected Ids(EmployeeID, TerritorieID) of Employees and Territories to the connection table  EmployeeTerritories. Each time the selected Territories must be highlighted and changes must be persisted.

   For that purpose two functions where created, ResolveList which highlights the Employee’s Territories on the ListBox and ResolveTable which persists the changes to the DataTable, with the same arguments. Ex. table: [EmployeeTerritories], listCtl: [listBox], listKey: "TerritorieID", key: "EmployeeID", value: [EmployeeID]. FindRow and ClearRow are accessory functions :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using System.Data;
using System.Web.UI.WebControls;

namespace CsLib
{
public class ConnectionLists
{
internal static DataRow FindRow(DataTable table, string[] keyNames, object[] keyValues)
{
table.DefaultView.Sort = string.Join(",", keyNames);
DataRowView[] rvs =
table.DefaultView.FindRows(keyValues);
return (rvs.Length > 0) ? rvs[0].Row : null;
}

internal static void ClearRow(DataTable table, DataRow row)
{
switch (row.RowState)
{
case DataRowState.Added:
table.Rows.Remove(row);
break;
case DataRowState.Modified:
row.Delete();
break;
case DataRowState.Unchanged:
row.Delete();
break;
}
}

public static void ResolveTable(DataTable table, ListControl listCtl, string listKey, string key, object value)
{
foreach (ListItem i in listCtl.Items)
{
object[] values = new object[] { value, i.Value };
DataRow row = FindRow(table, new string[] { key, listKey }, values);
if (i.Selected && row == null)
table.Rows.Add(values);
else if (!i.Selected && row != null)
ClearRow(table, row);
}
}

public static void ResolveList(DataTable table, ListControl listCtl, string listKey, string key, object value)
{
foreach (ListItem i in listCtl.Items)
{
object[] values = new object[] { value, i.Value };
DataRow row = FindRow(table, new string[] { key, listKey }, values);
if (row != null) i.Selected = true;
}
}
}
}

   What actually those functions do is deleting and adding rows to DataTable and selecting the appropriate ListItems respectively.

   The F# code follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#light

module FsLib.ConnectionLists

open System.Data
open System.Web.UI.WebControls

let internal FindRow (table :DataTable) keys (values :obj[]) =
table.DefaultView.Sort <-
String.concat "," keys
let
rows = table.DefaultView.FindRows(values)
match rows.Length with
| 0 -> None
| _ -> Some rows.[0].Row

let internal ClearRow (table :DataTable) (row :DataRow) =
match row.RowState with
| DataRowState.Added -> table.Rows.Remove(row)
| DataRowState.Modified -> row.Delete()
| DataRowState.Unchanged -> row.Delete()
| _ -> ()

let public ResolveTable (table :DataTable) (listCtl :ListControl) listKey key value =

let commit li =
match li with
| (v,true) -> let row =
FindRow table [|key;listKey|] [|value;(v:>obj)|] //Add selection
match
row with
| None -> do table.Rows.Add(
[|value;(v:>obj)|] )
| Some
row -> ()
| (v,false) ->
let row = FindRow table [|key;listKey|] [|value;(v:>obj)|] //Remove selection
match
row with
| None -> ()
| Some
row -> ClearRow table row
[| for i in listCtl.Items -> (i.Value, i.Selected) |] |> Array.iter
commit

let public ResolveList (table :DataTable) (listCtl :ListControl) listKey key value =
let select values =
let
row = FindRow table [|key;listKey|] values
match
row with
| None -> ()
| Some
row -> listCtl.Items.FindByValue(row.[listKey].ToString()).Selected <- true
[| for i in listCtl.Items -> [|value;(i.Value:>obj)|] |]
|> Array.iter select

   At first glance the lines of code are significantly less, 30% more lines C# code. Use of .NET OO libraries is apparent besides the functional code. Also the absence of if statements. There are examples of options (ln. 8), pattern matching, sequence expressions(ln. 40) and core list library functions. ResolveList starts from ListItems having in mind logical delete.
In the source code project bellow the consuming of F# library is transparent (In order to run the sample you will need F# CTP with VS 2008 or VS 2010b).

   F# integrates with legendary .NET libraries and OO code giving the ability for functional style to be used. The intention of the code is more accurate with less typing in trivial flows & statements.