Πέμπτη, 20 Αυγούστου 2009 12:05 πμ
napoleon
Real world F#
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.