F#でSQLite

今回はF#でSQLiteを利用してみたいと思います。

System.Data.SQLite入手

NuGetで下記のパッケージを取得します。
f:id:any-programming:20180226162251p:plain

データベース準備

sample.sqliteファイルに、下記のテーブルを定義しています。

作成は「DB Browser for SQLite」や、IDEに付属のツール、上記のdllを利用してプログラミング等で行えます。

personテーブル

id INTEGER PRIMARY KEY AUTOINCREMENT
name TEXT
age INTEGER


System.Data.SQLite単体で利用

生のSQL文を作成して実行しています。

望んだままのSQLを発行することが可能です。

DataSourceにはsample.sqliteのパスを指定してください。

let connectionString = SQLiteConnectionStringBuilder(DataSource = "sample.sqlite").ToString()
use connection = new SQLiteConnection(connectionString)
connection.Open()

// レコード追加
using (connection.CreateCommand())
    (fun command ->
        command.CommandText <- "INSERT INTO person (name, age) VALUES (@name, @age)"
        [ SQLiteParameter("@name", "Jerald")
          SQLiteParameter("@age",  40)
        ]
        |> List.iter (command.Parameters.Add >> ignore)
        command.ExecuteNonQuery() |> ignore
    )

// クエリ
using (connection.CreateCommand())
    (fun command ->
        command.CommandText <- "SELECT * FROM person WHERE 10 < age"
        use reader = command.ExecuteReader()
        while reader.Read() do
            printf "id:%A name:%A age:%A\n" reader.["id"] reader.["name"] reader.["age"]
    )    
connection.Close()    


LINQ to SQLを利用

SQL文の作成に、IQueryableを利用します。

これによりSQL文の間違いをコンパイル時に見つけやすくなります。

参照にSystem.Data.Linq.dll、System.Data.dll、System.Transactions.dllを追加してください。

AUTOINCREMENTを利用できるよう、IDはNullableで定義してあります。

[<Table>]
type Person() =
    [<Column(IsPrimaryKey = true)>]
    member val ID = Nullable<int>() with get, set
    [<Column>]
    member val Name = "" with get, set
    [<Column>]
    member val Age = 0 with get, set

let connectionString = SQLiteConnectionStringBuilder(DataSource = "sample.sqlite").ToString()
use connection = new SQLiteConnection(connectionString)
use context = new DataContext(connection)
    
// レコード追加
context.GetTable<Person>().InsertOnSubmit(Person(Name = "Jerald", Age = 40))
context.SubmitChanges()
    
// クエリ
query
    { for person in context.GetTable<Person>() do
        where (10 < person.Age)
    }
|> Seq.iter (fun x -> printf "id:%A name:%A age:%A\n" x.ID.Value x.Name x.Age)


SQLProvider利用

テーブルクラスの生成を、TypeProviderを利用して行うようにしてみます。

下記NuGetを取得します。
f:id:any-programming:20180226164225p:plain

SqlDataProviderに、自動生成対象のデータベースのパスを指定します。

[<Literal>]
let connectionStringCompileTime = "Data Source=sample.sqlite;Version=3"
type Database =
    SqlDataProvider<
        DatabaseVendor = Common.DatabaseProviderTypes.SQLITE,
        ConnectionString = connectionStringCompileTime
    >

let connectionString = SQLiteConnectionStringBuilder(DataSource = "sample.sqlite").ToString()
let context = Database.GetDataContext(connectionString)
        
// レコード追加
let person = context.Main.Person.Create()
person.Name <- "Jerald"
person.Age  <- 40L
context.SubmitUpdates()

// クエリ
query 
    { for person in context.Main.Person do
        where (person.Age > 10L) // 10L < person.Age では動作しない…
    }
|> Seq.iter (fun x -> printf "id:%A name:%A age:%A\n" x.Id x.Name x.Age)