C#でプラグイン機能を開発に挑戦してみた(2)
LiteDBでプラグインを管理してみる

どもです。

前回のエントリで、C#でプラグインを開発し、かつ開発したプラグインを使うことができるようになりました。
ところで、前回のエントリで開発/使用したプラグイン(DLL)は、ファイル名やパスが固定でした。
しかし実際のプラグインについては、ツールに登録されているプラグインの名前や、そのプラグインファイルのパスを管理する必要があります。
そこで今回は、LiteDBを使用してプラグインの管理をしてみたので、それについて書きます。

1. 開発環境

毎度ですが、開発環境です。

開発/実行環境
項目 内容
OS Windows10 Pro(1909)
CPU i7-8700
メモリ 16GB
IDE Visual Studio Community 2019
Version 16.11.2
.NET .NET Framework 4.7

2. LiteDBとは

今回使用するLiteDBは、以下のような特徴を持ちます。

  • C#で実装されている
  • 単一ファイルのデータベース
  • NoSQL
  • 高速で軽量

似たようなデータベースとして、SQLiteがあります。
もちろんSQLiteを採用しても問題ありません。
しかし、今回の内容ではリレーショナルデータベースである必要は全くありません。
テーブル設計なども必要ないため、LiteDBの方がより適当であると判断しました。

3. LiteDBを使ってみよう

3.1. LiteDBのインストール

LiteDBは、VisualStudioのNuGetパッケージマネージャ/パケットマネージャーコンソールを使用してインストールします。
インストールの際には、以下のコマンドを実行します。

PM> Install-Package LiteDB

なお、公式のダウンロードページでは、以下のようなコマンドを実行することが推奨されています。

PM> Install-Package LiteDB -Version 5.0.11

「-Version」オプションを使用してインストールするバージョンを指定できるようようです。
なお今回は、「-Version」を使用しないでインストールを行いましたが、Ver.5.0.11がインストールができていました。

3.2. 使ってみよう

インストールが完了したら、さっそく使ってみます。
プラグインの管理ですが、簡単にするために、LiteDBで以下の項目のみを管理することを考えます。

  • プラグインの名前(タイトル)
  • プラグインのパス

これらの情報を保持するクラスとして、Pluginクラスを以下のように定義します。

public class Plugin
{
	public int id { get; set; }

	public string Title { get; set; }

	public string FileName { get; set; }
}

ここで変数idは、テーブルで管理する番号です。
意図して値を設定したりしませんが、管理上必要なので、追加しています。
TitleおよびFileNameは、名前そのままです。
まず、プラグインの登録です。
ココでは、前回のエントリで作成したプラグインの情報を登録します。
登録は、以下のコードで行います。

public void RegistSampleData()
{
	using (var db = new LiteDatabase(@"PluginDb.db"))
	{
		var col = db.GetCollection("plugindb");
		var pluginA = new Plugin()
		{
			Title = "PluginA",
			FileName = "PluginA.dll"
		};
		col.Insert(pluginA);
		var pluginB = new Plugin()
		{
			Title = "PluginB",
			FileName = "PluginB.dll"
		};
		col.Insert(pluginB);
	}
}

ここで「PluginDb.db」は、LiteDBのファイル名です。
また「plugindb」が、プラグイン管理用のテーブル名です。
LiteDBでは、LiteDatabaseクラスのコンストラクタで指定したファイルが存在しない場合には、自動で対応するファイルが生成されます。
また、GetCollection()メソッドで指定したテーブルがデータベースに存在しない場合、自動で対応するテーブルが生成されます。
そのため、(存在しない)データベースファイルやテーブルへのアクセスを行っても、エラーになったり、例外になったりすることはありませんでした。

次に、データベースからの読出しです。
上述のコードで書き込んだ情報をデータベース/テーブルから取得する場合は、以下のコードを実行します。

public Plugin[] GetPluginInfos()
{
	using (var db = new LiteDatabase(@"./../db/PluginDb.db"))
	{
		var col = db.GetCollection("plugindb");
		var query = col.Query().ToEnumerable().ToArray();
		return query;
	}
}

GetCollection()メソッドでテーブルの内容を取得します。
このメソッドは、「ILiteCollection」型のデータを返します。
このままでは、Plugin型(クラス)の情報にアクセスできません。
このデータを、アクセス可能なPlugin型(の配列)にするために、

Query()→ToEnumerable()→ToArray()

の順番でメソッドを連続で実行しています。
ここまでの操作により、データベースに登録されたデータに対応するPluginオブジェクトが取得できます。

3.3. プラグインを実行してみよう

LiteDBに登録されたプラグイン情報を取得できることが分かったので、次は実際にプラグインの処理を実行できるか確認してみます。
Pluginオブジェクトの情報を元に、実際のプラグイン(IPluginインターフェースを実装したオブジェクト)を取得、処理を実行できるか確認してみます。
LiteDBから取得した情報(今回は「Plugin」オブジェクト)から、前回実装したプラグイン(DLL)のオブジェクトを取得してみます。
なお、プラグインのDLLファイルは、実行ファイルと同じフォルダに格納しています。

public IPlugin Load(Plugin plugin)
{
	string pluginPath = $@"./{plugin.FileName}";
	var assembly = Assembly.LoadFrom(pluginPath);
	var ifType = assembly.GetTypes()
		.Where(t => t.IsClass && t.IsPublic && !t.IsAbstract && (t.GetInterface(nameof(IPlugin)) != null))
		.FirstOrDefault();
	if (null == ifType)
	{
		return null;
	}
	IPlugin pluginInstance = (IPlugin)Activator.CreateInstance(ifType);
	return pluginInstance;
}

この関数で取得したプラグインが実行できるか否かは、プラグインの関数を実行してみて確認します。
プラグインの取得から実行までは、以下のコードで実行します。

public class PluginSample
{
	public static void Main()
	{
		PluginManager manager = new PluginManager();
		Plugin[] pluginDatas = manager.GetPluginInfos();

		IPlugin plugin = manager.Load(pluginDatas[0]);
		PluginOutput pluginOutput = plugin.PluginFunction("PLUGIN MANAGE SAMPLE A");
		Console.WriteLine(pluginOutput.message);

		plugin = manager.Load(pluginDatas[1]);
		pluginOutput = plugin.PluginFunction("PLUGIN MANAGE SAMPLE B");
		Console.WriteLine(pluginOutput.message);

		return;
	}
}

このコードを実行すると、以下のような結果が得られます。



このように、目的のプラグイン(DLL)を取得/実行できていることが確認できています。

4. まとめ

今回は、LiteDBを用いたプラグインの管理について書きました。
プラグインの管理では、データベースで扱う項目が非常に少なく、またテーブルもシンプルにできます。
このことから、データベースにLiteDBを選択してみました。
このデータベースに登録されたプラグイン情報から、対応するDLLをロードして、プラグインの処理を実行できるか確認してみました。
その結果、DLLをロードし、処理を実行できることが分かりました。

今後、プラグイン機能を持つアプリケーションを開発する際には、LiteDBのようなシンプルなデータベースが良いのではないかと思います。

ではっ!

ex.公開しています

今回のエントリで紹介したコードは、全てGitHubで公開しています。
最後の動作確認で実際に使用したプラグイン(DLL)以外は、本文中に記載したコードと違いはありません。
が。
それでも参考、一助になれば幸いです。