かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

F# 2.0でEntity Framework 4.1 CodeFirst その2「関連」

前回の例が単一テーブルだったので複数テーブル間の関連があるケースもやってみました。ちょっとはまったのは、F#って前方参照できないのでEmployee <-> Department間の相互参照させることでした。andで繋げてクラス定義すると前方参照もできるようになるみたいです。つまり1ファイルに必ずまとめて定義しろよってことみたいですね。(間違ってたらコメントください)

ということでさくっとコードだけ載せておきます。

namespace SampleApplication

    open System
    open System.ComponentModel.DataAnnotations
    open System.Data.Entity
    open System.Data.Objects.DataClasses
    open System.Collections.Generic

    // クラスは名前空間に所属させる
    type Department() =
        let mutable id = 0
        let mutable name = ""

        let mutable employees : ICollection<Employee> = null

        member x.ID
            with get() = id
            and set v = id <- v

        member x.Name
            with get() = name
            and set v = name <- v

        abstract Employees : ICollection<Employee> with get, set

        default x.Employees
            with get() = employees
            and set v = employees <- v

    // F#は前方参照したいときはandで繋いで定義するらしい・・・
    and Employee() =

        let mutable id = 0
        let mutable name = ""
        let mutable department : Department = Department()

        member x.ID
            with get() = id
            and set v = id <- v

        member x.Name
            with get() = name
            and set v = name <- v

        abstract Department : Department with get, set

        default x.Department
            with get() = department
            and set v = department <- v

    type EduContext() =
        inherit DbContext()

        // valとDefaultValueを使って未初期化でもOKなように細工
        [<DefaultValue>]
        val mutable employee : IDbSet<Employee>

        [<DefaultValue>]
        val mutable department : IDbSet<Department>

        member public x.Employees
            with get() = x.employee 
            and set v = x.employee <- v

        member public x.Departments
            with get() = x.department
            and set v = x.department <- v

    module Test = 
        [<EntryPoint>]
        let main(_) =
            Database.SetInitializer(
                DropCreateDatabaseIfModelChanges<EduContext>())

            // あとは普通に使える
            use ctx = new EduContext()
            let dept = Department(Name = "人事部")
            ctx.Departments.Add(dept) |> ignore
            // 追加
            ctx.Employees.Add(Employee(Name = "田中 太郎", Department = dept)) |> ignore
            let updateCount = ctx.SaveChanges()
            printfn "%d" updateCount

            // 再読み込み
            ctx.Departments.Load()
            // 取得データの表示
            ctx.Departments
                |> Seq.iter (fun d -> 
                                printfn "%d %s" d.ID d.Name
                                // 部署にぶらさがってる従業員データも表示
                                d.Employees |> Seq.iter (fun e -> printfn "  %d %s" e.ID e.Name))

            0