C#のドライバでつないでたMongoDBサーバを変更したらよくつながらなくなったのとその対処

 このブログのデータベースはMongoDBのホスティングサービスmlabを使っていたのだが、スペックとコストなどの問題から、VPSに入れたものへ切り替えた。そうしたらデータが取ってこれなくなることが頻発した。

 このブログはASP.NET Core MVCで作ってある。アプリのプロセス立ち上がり時にデータベース接続を行い、以降はその接続を使いまわしてリクエストをさばいていた。
client = new MongoClient(clientSettings);

db = client.GetDatabase(dbName);


 調べてみたところ、接続時のオプションにkeepAliveなるものがあるようなので、それを使ってみた。問題は起きなくなった。
https://jira.mongodb.org/browse/CSHARP-1151
var url = new MongoUrl(connectionString);

var clientSettings = MongoClientSettings.FromUrl(url);
Action<Socket> socketConfigurator = s => s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
clientSettings.ClusterConfigurator = cb => cb.ConfigureTcp(tcp => tcp.With(socketConfigurator: socketConfigurator));
client = new MongoClient(clientSettings);
db = client.GetDatabase(dbName);


 アプリ観察によると問題はとりあえず解決したようなんだけどちょっともやもや。MongoDBサーバをホスティングサービスのものから自分のVPS運用のものに変えてから問題は出始めた。なのにクライアントサイドの接続コードを変えることで問題は解決した。ちょっと謎。
comment: 0

C#でMongoDBでのサーバーサイドJavaScriptを実行する

 コレクションの集計処理をしたかったのだが、MapReduceでは望みの形に集計結果をまとめられなかった。だからサーバーサイドJavaScriptでやることにした。ただしサーバーサイドJavaScriptはセキュリティの懸念やパフォーマンスの注意点があるので気軽に使うものではない。今回はそれらが問題として出てこないので使うことにした。

 まずC#コード。サーバーサイドで実行するJavaScriptも埋め込んだ。
//using MongoDB.Driver;

//using MongoDB.Bson;
//using MongoDB.Driver.Core.Operations;
//using MongoDB.Driver.Core.WireProtocol.Messages.Encoders;
//using MongoDB.Driver.Core.Bindings;
//using System.Threading;

class Program
{
private static IMongoClient client;
private static IMongoDatabase DbConnection;

static void Main(string[] args)
{
client = new MongoClient("mongodb://127.0.0.1");
DbConnection = client.GetDatabase("helicon");

AggregateTag();
}

public static void AggregateTag()
{
var databaseName = new DatabaseNamespace("helicon");

var code = (BsonJavaScript)@"
function () {
// do something
}";

var messageEncodingSettings = new MessageEncoderSettings();
var operation = new EvalOperation(databaseName, code, messageEncodingSettings);
var source = new CancellationTokenSource();
var token = source.Token;
var writeBinding = new WritableServerBinding(client.Cluster);
operation.Execute(writeBinding, CancellationToken.None);
}
}


 あとはサーバサイドでJavaScriptを実行するために、接続ユーザに実行権限を持たせる。
Allow user to execute eval() command on MongoDB 3.x

データベースに管理権限者でログイン。そしてadminデータベースへ。
use admin;


ロールを作成。
db.createRole( { role: "executeFunctions", privileges: [ { resource: { anyResource: true }, actions: [ "anyAction" ] } ], roles: [] } )


ロールを接続ユーザに付加。
db.grantRolesToUser("someone", [ { role: "executeFunctions", db: "admin" } ])


付加されたことを確認。
db.getUser("someone") 


これで権限が付いたので、C#コードを実行すればMongoDBのサーバサイドJSが実行される。

 このブログのサイドバーの、タグや投稿月毎の記事のカウントにこの、MongoDBのサーバサイドJSを使っている。
comment: 0

C#で基底クラスの静的メソッドから、メソッドを実行している派生クラスの型を知りたかったのでジェネリックを使った

 前回に基底クラスの静的メソッドから、メソッドを実行している派生クラスの型を得るのに失敗した。今回はそれをジェネリックを使って解決する。

 ジェネリックってなに?というのはもうだいぶ前からあるものなので、ちゃんとした説明は他ページを参照。
http://ufcpp.net/study/csharp/sp2_generics.html
”さまざまな型に対応するために、型をパラメータとして与えて、その型に対応したクラスや関数を生成するもの”
 ↑が参考にしたページに載っていたジェネリックの説明。例えば、この一例として「型をパラメータとして与えて、その型に対応した関数を生成する」ことを考える。ある一つの関数の定義として、パラメータで型を与える。この関数の定義にジェネリックを利用すれば、この関数の定義は一つなのに、関数からの返り値をパラメータで与える型に合わせて変えていくことができる。動的型付け言語をやってきた人なら違和感はないだろうが、静的型付け言語をやってきた人なら、お、となるところではないだろうか。その辺の例は参照先にあるのでここでは割愛。

 ジェネリックを利用して、基底クラスで定義された静的メソッドの中から、そのメソッドが実行される派生クラスの型を得る。「型をパラメータとして与えて、関数内でその型を利用する」ということをやる。なんで型が欲しいかってのは、リフレクションをやりたいからなの。

class Program

{
static void Main(string[] args)
{
Console.WriteLine(Class1.GetDerivedType<Class1>());
}
}

public class Class1 : BaseClass1
{
}

public abstract class BaseClass1
{
public static string GetDerivedType<T>() where T : BaseClass1
{
var t = typeof(T);
return t.Name;
}
}


 上記を実行すれば、"Class1"が出力される。これでClass1にもっといろいろ定義してあれば、リフレクションを使ってもろもろ読み出すことができる。でも個人的に読み出しの形をメソッドではなくプロパティにしたかったので書き換え。

class Program

{
static void Main(string[] args)
{
Console.WriteLine(Class2.DerivedType);
}
}

public class Class2 : BaseClass2<Class2>
{
}

public abstract class BaseClass2<T>
{
public static string DerivedType
{
get
{
var t = typeof(T);
return t.Name;
}
}
}

 これでもメソッドでやってたのと等価な結果となる。静的クラスで定義されたメンバから、派生クラスで実行時の型が使えるようになったので問題解決。

 あらためて派生クラスを見返してみると、変な形になってるんじゃないかと思った。けどもこれはデザインパターンの定石の一つとして、「奇妙に再帰したテンプレートパターン」という名前がついているようだ。
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
More C++ Idioms/奇妙に再帰したテンプレートパターン(Curiously Recurring Template Pattern)
comment: 0

C#で基底クラスの静的メソッドから、メソッドを実行している派生クラスの型を知りたかった

 C#で基底クラスとその派生クラスがある。基底クラスに定義した静的メソッドを派生クラスのインスタンスで実行したとき、静的メソッド内で派生クラスの型情報を得たかった。それが無理だったやり方のメモ。成功したやり方は後日の記事であらためて。

 なんで型が欲しかったかっていうと、リフレクションをしたかったから。そんなところで、下記コードを書いた。BaseClassのメソッド内でthisがあるが、これはもちろん静的メソッド内なので使えない。ここが問題となった。

class Program

{
static void Main(string[] args)
{
AClass.GetDerivedType();
}
}

public class AClass : BaseClass
{
}

public abstract class BaseClass
{
public static void GetDerivedType()
{
var t = typeof(this);
Console.WriteLine(t);
}
}

静的メソッド内ではthisを使えない。じゃあどうする?BaseClassをthisの代わりに入れたら、派生クラスで実行しても得られるのはBaseClassになってしまう。この静的メソッドの定義を基底クラスでやるのやめる?派生クラスでいちいちこのメソッドの定義書いていくのはナンセンスだと思う。派生クラスを指してくれるキーワードあったっけ…たぶんない。スタックオーバーフローでも無理だと書かれていた。
Get derived type from static method

 じゃあどうするの、ジェネリックを使うのというのを次回に。
comment: 0

MongoDBのコレクションインデックスをC#で追加(TTLの設定など)

 C#でTTLの設定などをやっているサンプルがなかなか見つからなかったのでメモ。
 まず必要なパッケージなど。
using MongoDB.Bson;

using MongoDB.Driver;
using System;
using System.Collections.Generic;


 あとは接続からインデックス作成まで。
var client = new MongoClient();

var db = client.GetDatabase("test");

var collectionName = "Foo";
var keyName = "createdAt";

var fieldDefinition = new StringFieldDefinition<BsonDocument>(keyName);
var indexDefinition = new IndexKeysDefinitionBuilder<BsonDocument>().Ascending(fieldDefinition);
var options = new CreateIndexOptions()
{
ExpireAfter = TimeSpan.FromSeconds(10)
};


db.GetCollection<BsonDocument>(collectionName)
.Indexes.CreateOne(indexDefinition, options);


var models = new List<CreateIndexModel<BsonDocument>>();
var model1 = new CreateIndexModel<BsonDocument>(indexDefinition, options);
models.Add(model1);

db.GetCollection<BsonDocument>(collectionName)
.Indexes.CreateMany(models);
comment: 0