MongoDBのデータをC#のオブジェクトにマッピングして使う:ORマッパー

 このブログのデータベースはMongoDBを使っている。これまでデータベースから引っ張ってきたドキュメント(RDBでいうところの行データ)は、引っ張ってきた生の状態ではJSONなので、そこからC#の適切な型にキャストされるように手書きしていた。

 たとえば記事データは下記のようなプロパティを持ったオブジェクトで表していた。
public int? EntryID { get; set; } = null;


[DisplayName("タイトル")]
public string Title { get; set; }

[DisplayName("発行日時")]
public DateTime PublishDate { get; set; }

[DisplayName("本文")]
public string Body { get; set; }


 ↑のオブジェクトをJSONでサンプルを作ると下記。
{"EntryID":3, "Title":"今日の献立", "PublishDate":"2016 10:10:10",
"Tag":["夕食","料理"], "Body":"ほにゃらら"}

 で、↑のJSONをMongoDBからひっぱってきたときに、↑の↑にあるC#オブジェクトにキャストしていた。下記のように。
var doc = collection.Find<BsonDocument>(filter).FirstOrDefault();

if (doc == null)
{
return;
}

var entry = new Entry
{
Title = doc.GetValue("title").AsString,
Body = doc.GetValue("body").AsString,
EntryID = Convert.ToInt32(doc.GetValue("_id").ToDouble()),
PublishDate = doc.GetValue("publishDate").ToUniversalTime()
};

いちいちメソッド使ったりなんだで値をキャストしていくのが面倒だった。なのでORマッパを用意して下記のように書き換えた。一発。
var entry = collection.Find<Entry>(filter).FirstOrDefault();


 オブジェクトの定義は下記のようになった。
[BsonId]

[BsonElement("publishDate")]
[BsonRepresentation(BsonType.Int32)]
public int? EntryID { get; set; } = null;

[DisplayName("タイトル")]
[BsonRepresentation(BsonType.String)]
[BsonElement("title")]
[BsonRequired]
public string Title { get; set; }

[DisplayName("発行日時")]
[BsonRepresentation(BsonType.DateTime)]
[BsonElement("publishDate")]
[BsonRequired]
public DateTime PublishDate { get; set; }

[DisplayName("タグ")]
[BsonRepresentation(BsonType.String)]
[BsonElement("tag")]
public List<string> Tag { get; set; }

[DisplayName("本文")]
[BsonRepresentation(BsonType.String)]
[BsonRequired]
[BsonElement("body")]
public string Body { get; set; }

注意として、MongoDBがスキーマレスなのに反してC#は基本的に静的型付けであるから、C#側で未定義なキーがJSON側にあるとエラーでデータがマッピングできない。あと保存しないプロパティにはBsonIgnore属性をつける必要がある。



*以下ひとりごとメモ
ORマッパを使いたい
↑データ保存、読み出しでオブジェクト指向とのギャップを感じる
↑アプリケーションを書いている言語(非SQL)とは別の言語(SQL)が混ざってくる
↑行のデータを row['title'] のような表現で読みだしてるとか、ほんとうにデータベースを使ってる感が出てくる。
↑さらに、安全にやるにはプレースホルダを置いて、値をバインドしてということをシコシコとやる必要がある。値のバインドは非SQLな言語側での話。非SQLが安全なSQL構築の手助けをしてあげなきゃいけない。データを操作するためのSQLを構築するために非SQLでSQLを構築していかなければならない
↑結局コードがSQL、非SQLでごちゃごちゃしてくる
↑文字列型でSQL文を書いてそこにパラメータをバインドして、ってのがイケてない。セキュリティを確保しつつなんかもっと楽な手段はないか(LINQとかイケてると思う)。
↑データベースを使ってることをもうちょい隅のほうに追いやるとか隠ぺいとかできんか

データベース使って、そもそもなにをしたかったの?
→データの永続化だよ
→"オブジェクトを自由に冷凍保存して、自由に解凍しているような感覚" http://blog.jnito.com/entry/20100528/1274998429
→オブジェクト指向したいやつには最高じゃねーか
→ORマッパなしで書いていてオブジェクト指向との乖離を感じる。ORマッパを使っているサンプルコードを見ると、完全なのぞみは叶っていないにせよ、かなり好みのところまでいってる

 ORマッパの書き方を学ぶのがめんどうでベタベタで書いていたが、いろいろ書いていてベタベタなほうが面倒だと思うようになってきた。row['title'] のような辞書型系統での値の読出しや、ここに入っている値の非SQLのほうの型へのキャストをシコシコと書くのとおさらばしたかった。これからはORマッパさんとまじめにお付き合いしていこうと思う。
comment: 0