前回はインストールしました。今回は実際にデータを追加します。

Introduction

MongoDB は親切にも接続のためのドライバを用意してくれてます。しかも.NET用かつNuget経由で。素晴らしい。
恐ろしいことに、C、C++、C#、Java、Node.js、Perl、PHP、Python、Motor、Ruby、Scala用のドライバがオープンソースで用意してあります。
MongodDBおそろしい子! 今回もきちんと公式ページを見てやります。ちゃんと英文訳します。参考にしてください。
…ちゃんと英語ドキュメント見ないと勉強になりませんから(白目)

Explanation

開発環境構築

何はともあれ、Nugetです。
Nugetでインストールは、GUIでもできますが、GUIだとMongoDBが2つ出てきます。片方は古いMongoDBのドライバです。
nuget

コンソールで

1
Install-Package MongoDB.Driver

とやると確実です。
ちなみに、前回は最新版のMongoDB (執筆時点で3.2.0)をインストールし、今回はドライバのバージョンが2.2ですが、ちゃんと繋がるの?という方、C# and .NET MongoDB Driver にて互換性について説明があります。
2.2は MongoDB 2.4MongoDB 2.6MongoDB 3.0MongoDB 3.2 に繋がります。うん安心。
ただし、.NETの互換性が非常にタイトです。
2.0(ドキュメントには2.2とは書いてない)は.NET 4.5とMono 3.xでしか動きません。
逆に1.10は.NET 3.5以降をサポートしています。しかも Mono 2.10Mono 3.x も。
また、MongoDBは、Visual Studio は2010、2012でビルドして、テストしたようです。2015でも、いけるよね?

接続実験

公式ページAuthenticate to MongoDB with the C# Driverに説明があります。ちなみに、1.8以降のみの説明です。
のっけから意味のわからないことが書いてあります。Internal AuthenticationExternal Authenticationとあります。
Internal Authentication

Internal authentication refers to accounts stored inside MongoDB. Currently, the only way to authenticate against internal accounts is to use the MongoDB Challenge Response mechanism, or MONGODB-CR. This is the default mechanism.

1
訳:Internal authenticationは、内部のMongoDBに保存されているアカウントを参照します。現在、内部アカウントに対する認証する唯一の方法は、MogoDB Challenge Response メカニズム、または MONGODB-CR を使うことです。これは既定のメカニズムです。

External Authentication

External authentication refers to credentials validated outside of MongoDB. The external authentication provider currently used is Kerberos. To authenticate to a MongoDB cluster using Kerberos, you must specify the GSSAPI mechanism and a user name. On Windows, it is also possible to provide the password. Fundamentally, the process for connecting with Kerberos is the same for Windows and Linux systems; however, Linux systems require the use of kinit to acquire the security credentials whereas Windows systems perform this action transparently using SSPI based on the account running the current process when a password is not used.

1
2
 訳:External authenticationは、外部のMongoDB検証された資格情報を参照します。外部認証プロバイダーは現在Kerberosが使用されます。Kerberosを使用したMongoDBクラスターへ認証するために、GSSAPIメカニズムとユーザ名を必ず使用します。Windowsでは、パスワードを提供することも可能です。
基本的には、Kerberosによる接続プロセスは、WindowsとLinuxでのそれと同一です。しかし、Linuxではセキュリティ認証を得るためにkinitの使用を、一方でWindowsでは、パスワードを利用しないとき、現在のプロセスを実行しているアカウントに基づくSSPIを利用した透過的なアクションを実行します。

とあります。External authenticationのWindowsに対する最後の説明は意味不明ですが、要するに、サービスとして動いているローカルのMongoDBへのアクセスは、Internal、といことでしょう。きっと。Kerberosなんて言われても困ります。
で、Internalにのみ目を向けると、サンプルコードがあります。

var credential = MongoCredential.CreateMongoCRCredential(“test”, “user1”, “password1”);

var settings = new MongoClientSettings

{

Credentials = new[] { credential }

};

var mongoClient = new MongoClient(settings);

または、

var connectionString = “mongodb://user1:password1@localhost/test”;

var mongoClient = new MongoClient(connectionString);

はぁ?パスワード?と思ったあなた。ご安心を、パスワードはなくても接続できます。
前回は設定していませんしね(白目)

1
var mongoClient= new MongoClient("mongodb://localhost");

これでOKです。
まずは前回設定したサービスを起動します。
で、起動後、下記のプログラムを実行します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using MongoDB.Driver;

namespace MongoDB2
{
class Program
{
static void Main(string[] args)
{
var mongoClient = new MongoClient("mongodb://localhost");
foreach (var document in mongoClient.ListDatabases().ToList())
{
Console.WriteLine("Document:{0}", document);
foreach (var name in document.Names)
{
Console.WriteLine("Name:{0}", name);
}
}

Console.ReadKey();
}
}
}

実行後、コマンドプロンプトに

1
2
3
4
Document:{ "name" : "local", "sizeOnDisk" : 65536.0, "empty" : false }
Name:name
Name:sizeOnDisk
Name:empty

と表示されるはずです。
データベースとかを既に追加していたら違う結果になると思いますが。
サービスが起動していないと、

タイムアウト

って出ます。内容を見る限りタイムアウトは30秒のようです。
とりあえず接続試験はOKです。

データベースって何?

先のコードで、

1
foreach (var document in mongoClient.ListDatabases().ToList())

って書きました。
ListDatabases ってあるのに変数名がdocumentとか、センスないの?馬鹿なの?って思った人いるかもしれませんが、これは ListDatabases メソッドの戻りが、MongoDB.Driver.IAsyncCursor<MongoDB.Bson.BsonDocument> だからです。
Bsonって何?というと、Wiki先生はこう仰っています

BSONは主にMongoDBのデータストレージ及びネットワーク転送フォーマットとして利用されている、データ交換フォーマットである。

単純なデータ構造や連想配列(MongoDBではオブジェクトまたはドキュメントと表す)を示すバイナリ構造であり、 名称はJSON由来であり”バイナリ型JSON”の略語である。

よーするにMongoDB用のデータ構造でJsonのバイナリ版だと。
で、BsonDocumentって何?って調べても、Represents a BSON document. としか書いてない。
これがデータベース?と思いきや、MongoClient.GetDatabase メソッドというのがあって、これの戻り値は MongoDB.Driver.IMongoDatabase となっていて、データベースは存在するよう。
じゃぁ、さっきのListDatabases メソッドはっていうと、Lists the databases on the server. って言っている。
これはAPIとしてどうなの?という感じがしないでもない。
MongoClient クラスには、DropDatabaseはあるから、作成はあっても良さそうな気がする。

データベースを作る

先の ListDatabases メソッドで戻ってきた BsonDocument はきっと初期状態で入っている、管理用のデータベースの中身、と仮定します。
なので、データベースを作れば結果が変わる、と信じてデータベースを作ってみます。
が、そんなAPIがない、というのは前章のとおり。
というわけでGoogle先生に聞いてみた。
公式にもない、というか見つからなかったが、Creating a simple database in MongoDB with C# によると、

The line server.GetDatabase(“MyDatabase”) will get the database (if it exists) but also create a database if it doesn’t exist. Note: if you are creating a database using GetDatabase it will not exist until you actually store data in it.

1
2
3
 訳: server.GetDatabase(“MyDatabase”) の行はデータベース (存在すれば) を取得しますが、存在しなければデータベースを作ったりもします。

ノート:GetDatabaseでデータベースを作ったとしても、データを突っ込むまでは、データベースは存在しません。

って言ってます。
必要になるまでテーブルは要らない、ってことですか。

データを追加する

データベース「だけ」を作る方法はないので、データを設定してみます。
さっきのページに、親切にも同じようなことが書いてあります。

Like the creating of the database, if no employees currently exist we still get a collection object which we can then save data to.

1
訳: データベースの作成のように、もしemployeesがそのときに存在しなければ、データを保存することのできるコレクションオブジェクトを取得します。

とある。サンプルを見ると、

public class Person { public ObjectId Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public int Age { get; set; } } MongoClient client = new MongoClient(); MongoServer server = client.GetServer(); MongoDatabase db = server.GetDatabase(“MyDatabase”); MongoCollection collection = db.GetCollection(“employees”); Person p = new Person { Id = ObjectId.GenerateNewId(), FirstName = “Bob”, LastName = “Baker”, Age = 36 } collection.Save(p);

とあるので、コレクションも必要になるまで反映されない、ってことですか。
というか、データベースの配下にコレクションなのね。そーいう構造ですか。
で、作ったサンプルはこちら

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class Program
{
static void Main(string[] args)
{
var mongoClient = new MongoClient("mongodb://localhost");
//TestConnect(mongoClient);

InsertData(mongoClient);
TestConnect(mongoClient);

Console.WriteLine("Please enter any key...");
Console.ReadKey();
}

private static void InsertData(MongoClient mongoClient)
{
Console.WriteLine("InsertData");

var database = mongoClient.GetDatabase("Database1");

var c1 = new Company
{
Id = ObjectId.GenerateNewId(),
Name = "Microsoft",
Established = new DateTime(1975, 4, 4)
};

var c2 = new Company
{
Id = ObjectId.GenerateNewId(),
Name = "Apple",
Established = new DateTime(1976, 4, 1)
};

var collection = database.GetCollection<Company>("Companies");
collection.InsertOne(c1);
collection.InsertOne(c2);

var findFluent = collection.Find(FilterDefinition<Company>.Empty);
var count = findFluent.Count();
Console.WriteLine("Count:{0}", count);
}

public sealed class Company
{

public ObjectId Id
{
get;
set;
}

public string Name
{
set;
get;
}

public DateTime Established
{
get;
set;
}

}

private static void TestConnect(MongoClient mongoClient)
{
Console.WriteLine("TestConnect");

foreach (var document in mongoClient.ListDatabases().ToList())
{
Console.WriteLine("\tDocument:{0}", document);

foreach (var name in document.Names)
{
Console.WriteLine("\t\tName:{0}", name);
}
}
}

}

データの検索は、Find メソッドが使えることがわかりました。
で、先のBlogのページで使っていた Save メソッドはレガシーのAPI用だったようで、今は InserOne メソッドのでした。
ObjectId とかわかりませんし、シリアライズできないオブジェクトはどうなるの?とか色々ありますがまずはこれだけ。
実行結果はこんな感じ。

1
2
3
4
5
6
7
8
9
10
11
12
InsertData
Count:2
TestConnect
Document:{ "name" : "Database1", "sizeOnDisk" : 8192.0, "empty" : false }
Name:name
Name:sizeOnDisk
Name:empty
Document:{ "name" : "local", "sizeOnDisk" : 65536.0, "empty" : false }
Name:name
Name:sizeOnDisk
Name:empty
Please enter any key...

ドキュメントが増えただけ?

Conclusion

まだまだ、わからないことだらけです。手軽な感じはしますが、ちょっともやもやします。
人によっては、もっとシンプルな KVS (Key-Valueストア)Redis とかのいいのでしょうか?Redisよく知らないけど。

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/Database/MongoDB/MongoDB2