Soma - Sql Oriented MApping framework for F#

One of the things I haven't blogged about much is the great collection of F# projects up on CodePlex.

The one that caught my eye today is Soma - a "Sql Oriented Mapping Framework". (Note, Soma is documented in Japanese  - I have seen from Twitter and elsewhere the many F# tweets in Japanese, and its great to see a community developing there)

Soma implements an F#-oriented, code-first approach to O/R mapping. Queries are expressed in SQL strings, with some interesting use of F# quotations. Here's the description of the project from the home page, along with a sample that looks very compelling:

Soma is an O/R mapping framework develeped in F#.
Supported programming languages and RDBMS are followings:
SomaはF#で開発されたO/Rマッピングフレームワークです。
サポートされるプログラミング言語とRDBMSは次の通りです。
languages

  • F# 2.0
  • C# 4.0
  • Visual Basic 2010

RDBMS

  • Microsoft SQL Server 2008
  • MySQL 5.x.
  • Oracle Database 11g

Main Features

Main features are followings:

  • 2 way SQL - SQL is executable in programs and in sql tools out of the box
  • SQL log handling
  • automatic primary key generation
  • optimistic lock
  • pagination
  • support for F# immutable record type
  • support for muttable POCO
  • arbitrary SQL execution and result mapping
  • stored procedure call
  • no configuration file
  • stateless architecture

主要な機能は以下の通りです。

  • 2 way SQL - SQLはプログラムとツールで実行可能
  • SQLのログハンドリング
  • 主キーの自動生成
  • 楽観的ロック
  • ページング
  • F#のイミュータブルなレコード型のサポート
  • ミュータブルなPOCOのサポート
  • 任意のSQLの実行と結果のマッピング
  • ストアドプロシージャの実行
  • 設定ファイルを必要としない
  • ステートレスなアーキテクチャ

Sample Code in F#

Samples in other languages are included in the distribution zip file.
他の言語で作られたサンプルは配布zipファイルに含まれます。

 






open System<br>open System.Transactions<br>open Soma.Core<br>// define a module wraps Soma.Core.Db module<br>module MyDb =   let config = <br>    { new MsSqlConfig() with<br>      member this.ConnectionString = "Data Source=.;Initial Catalog=Soma.Tutorial;Integrated Security=True" }  let query<'T> = Db.query<'T> config  let queryOnDemand<'T> = Db.queryOnDemand<'T> config  let execute sql expr = Db.execute config sql expr  let find<'T when 'T : not struct> = Db.find<'T> config  let tryFind<'T when 'T : not struct> = Db.tryFind<'T> config  let insert<'T when 'T : not struct> = Db.insert<'T> config  let update<'T when 'T : not struct> = Db.update<'T> config  let delete<'T when 'T : not struct> = Db.delete<'T> config  let call<'T when 'T : not struct> = Db.call<'T> config<br>// define a record mapped to a table <br>type Employee = <br>  { [<Id(IdKind.Identity)>]<br>    EmployeeId : int     EmployeeName : string<br>    DepartmentId : int<br>    [<Version>]<br>    VersionNo : int }<br>// define a record mapped to a procedure<br>type ProcResultAndOut = <br>  { EmployeeId : int<br>    [<ProcedureParam(Direction = Direction.Output)>]<br>    EmployeeCount : int<br>    [<ProcedureParam(Direction = Direction.Result)>]<br>    Employees : Employee list }<br>let main =  // execute following code in a transaction, but don't commit<br>  use tx = new TransactionScope()    // find by id  let emp = MyDb.find<Employee> [1]<br>  printfn "FOUND RECORD : \n%A\n" emp  // update  let emp = MyDb.update { emp with EmployeeName = "Hoge" }<br>  printfn "UPDATED RECORD : \n%A\n" emp  // delete<br>  MyDb.delete emp<br>  printfn "DELETED RECORD : \n%A\n" emp  // insert  let emp = MyDb.insert { EmployeeId = 0; EmployeeName = "Allen"; DepartmentId = 2; VersionNo = 0}<br>  printfn "INSERTED RECORD : \n%A\n" emp  // query by SQL. parameters are bindable with "Code Quotations".   let empList = <br>    MyDb.query<string * Employee> "    select       d.DepartmentName, e.EmployeeId, e.EmployeeName, e.DepartmentId, e.VersionNo       from       Employee e <br>    inner join       Department d     on       e.DepartmentId = d.DepartmentId     where       e.DepartmentId = /* emp.DepartmentId */0 <br>    " <@ let emp = emp in () @><br>  printfn "QUERIED TUPLES : \n%A\n" empList  // call procedure  let result = MyDb.call<ProcResultAndOut> { EmployeeId = 1; EmployeeCount = 0; Employees = [] }<br>  printfn "PROCEDURE OUTPUT : \n%A\n" result  // exequte arbitrary SQL  let rows = <br>    MyDb.execute "<br>    delete from Employee <br>    " <@ () @><br>  printfn "AFFECTED ROWS : \n%A\n" rows<br>  Console.ReadKey()