Logo




Subscribe:
RSS 2.0 | Atom 1.0
Categories:

Sign In


[Giagnocavo]Michael::Write()

# Thursday, June 26, 2008
LINQ to the CRUD CreateTable generator in F#

With the DatabaseBase and TableHelper classes, you still have to generate a CreateTable field per table. Why do it by hand? I wrote an F# script to generate the statements. I must say, I'm loving F# more than I had hoped. The more I learn, the better it gets. I've noticed (even in C#) that using a functional style generally means less errors. This script worked without bugs the first time (i.e., as soon as it compiled), which is pretty cool (granted, it's not big, but I'm sure if I had tons of for loops, I woulda messed up somewhere). Maybe this weekend I'll try writing it in C# just to compare (pretty sure it'll be more than 59 lines!). And I'll preempt comments about readability: Yes, it may be difficult to read if you don't know F#, but more on that later...

I'd love feedback as to making it more "functional"; years of imperative programming don't die quickly. Also, I'm not very happy with the definition of chooseAttr, but I can't seem to get it to infer the type I want otherwise.

Anyways, here's the code. You'll need to specify the references when compiling: -r "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.Linq.dll" -r "C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0\System.Runtime.Serialization.dll"


    1 // crudcreatetable.fsx: Generates LINQ CRUD table fields using the horribly named DatabaseBase code

    2 //

    3 // Tables look like: [Table(Name="dbo.Accounts")]

    4 // Columns look like this:

    5 //  [Column(Storage="_AccountName", DbType="VarChar(128) NOT NULL", CanBeNull=false, IsPrimaryKey=true)]

    6 //  [DataMember(Order=1)] // Exists if serialization is turned on; used to order key parameters

    7 // Emits:

    8 //  public static readonly TableHelper<Account, String> Accounts =

    9 //      CreateTable(dc => dc.Accounts, a => a.AccountName);

   10 

   11 #light

   12 open System

   13 open System.Reflection

   14 open System.Data.Linq.Mapping

   15 open System.Runtime.Serialization

   16 

   17 let getAttr<'target, 'a when 'a :> ICustomAttributeProvider> (ty : 'a) =

   18     match List.of_array (ty.GetCustomAttributes(typeof<'target>, true)) with

   19         | a::_ -> Some (a :?> 'target)

   20         | [] -> None

   21 let chooseAttr<'target, 'a when 'a :> ICustomAttributeProvider> (ty : 'a) =

   22     match getAttr<'target,_> ty with

   23         | Some(a) -> Some(ty,a)

   24         | None -> None

   25 

   26 let joinStrings (sep:string) items = items |> Seq.fold1 (fun acc x -> acc + sep + x)

   27 let pluralize (name:string) = if name.EndsWith("s") then name else name + "s"

   28 

   29 let generate(asmpath:string) =

   30     let genTable (t:Type, tableName) =

   31         let keyProps =

   32             t.GetProperties()

   33             |> Seq.choose (chooseAttr<ColumnAttribute,_>)

   34             |> Seq.filter(fun (p,c) -> c.IsPrimaryKey)

   35             |> Seq.map(fun (p,_) -> p, getAttr<DataMemberAttribute, _> p)

   36             |> Seq.orderBy(function | _,Some(dm) -> dm.Order | _ -> 0)

   37             |> Seq.map (fun (p,_) -> p)

   38         let tw = new IO.StringWriter()

   39         let pn fmt = Printf.twprintfn tw fmt

   40         pn "public static readonly TableHelper<%s, %s> %s ="

   41             t.Name

   42             (joinStrings ", " (keyProps |> Seq.map (fun p -> p.PropertyType.Name)))

   43             tableName

   44         pn "\tCreateTable(dc => dc.%s, %s);"

   45             tableName

   46             (joinStrings ", " (keyProps |> Seq.map (fun p -> "a => a." + p.Name)))

   47         pn ""

   48         tw.ToString()

   49 

   50     let asm = Assembly.LoadFrom asmpath // Don't use ReflectionOnly 'cause it won't resolve dependencies

   51     asm.GetExportedTypes ()

   52         |> Seq.choose (chooseAttr<TableAttribute,_>)

   53         |> Seq.map (fun (t,ta) -> t, ta.Name.Replace("dbo.", "") |> pluralize)

   54         |> Seq.orderBy (fun (t,_) -> t.Name)

   55         |> Seq.map genTable

   56         |> Seq.fold1 (+)

   57 

   58 generate "C:\\yourlinq.dll"

   59     |> Console.WriteLine

FSharp
Thursday, June 26, 2008 5:13:44 AM UTC  #    Comments [0]  |  Trackback

OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):

Live Comment Preview