コンウェイとピーターの狭間で

ずっと放置しつつQiitaに移ったりしていましたが、スマートホームについて書いていこうかと

EntityFramework Code First+SQLiteでGuidを扱う

EntityFramework Code FirstでSQLite

EntityFramework Code Firstは、Microsoft謹製のORマッパーとして高機能でなかなか優秀です。 ただ、やはり相性がいいのはSQLServerだなと思っていたら、 いつの間にか、EntityFramework Code FirstでSQLiteが使えるようになっていました。

こののサイトが参考になります。

C# - Entity FrameworkでSQLiteのCodeFirstを利用する - Qiita

問題発生。GUIDが比較できない!?

モデルクラスは省略しますが、Guidで比較するクエリが正しく動作しません。

        var id = Guid.NewGuid();
        using (var context = new UserData())
        {
            context.Users.Add(new User() {Id = id });
            context.SaveChanges();
        }

        using (var context = new UserData())
        {
            var user = context.Users.First(_ => _.Id == id);   // 例外発生 「追加情報:シーケンスに要素が含まれていません」
        }

SQLiteのオプションで回避可能っぽい?

2時間ほど悩んだのですが、この辺の記事が参考になりました。

c# - How does the SQLite Entity Framework 6 provider handle Guids? - Stack Overflow

System.Data.SQLite: View Ticket

どうもSQLiteのEntityFramework用Providerのバグのようで、ConnectionStringに以下ように「BinaryGUID=True」を指定することで動作するようになるようです。

Data Source=c:\mydb.db;BinaryGUID=True;

いろいろ調べて、本質まで理解できていないと思うのですが、 どうもGuidは、EntityFramework+SQLiteでは、uniqueidentifier型に変換されるようで、uniqueidentifier型はBLOB 16バイトのようです。 そして、SQLiteではGuidはバイナリ形式であるX'0123456789ABCDEF0123456789ABCDEF'という形で指定するのですが、 単にGUIDのメモリをバイナリに変換してクエリしてしまったのでしょう。 GUIDは実際にはエンディアンを意識して変換しないといけないはずなので、その辺のバグなんですかね。