古いSystem.Data.SQLiteをMicrosoft.Data.SQLiteに移植する[暗号化もするよ]

まず、System.Data.SQLiteを使ってました。
NuGetなんて概念が生まれる前に古いSystem.Data.SQLite.dllをダウンロードして使い続けていたので、暗号化がサポートされていたんですよねー


これがNuGetで新しくしたらSetPasswordが使えないでやんの。
なんとか解決してきたので、情報をまとめておきます。

Microsoft.Data.SQLiteを入れるぞよ

最低限の環境用意

NuGetでMicrosoft.Data.SQLiteをインストールする。
依存関係が一式インストールされます。

SqliteConnection connection = new SqliteConnection();
string path = @"なんか良さげな場所\test.db";
bool isCreate = !File.Exists(path);
// Open
connection.ConnectionString = new SqliteConnectionStringBuilder("Data Source=" + path)
{
    Mode = SqliteOpenMode.ReadWriteCreate
}.ToString();
connection.Open();

// テーブル作ってみる
if (isCreate)
{
    SqliteCommand createTable = connection.CreateCommand();
    createTable.CommandText = "良さげなCREATE TABLE文;";
    createTable.ExecuteNonQuery();
    
    SqliteCommand insert = connection.CreateCommand();
    insert.CommandText = "良さげなINSERT INTO文;";
    insert.ExecuteNonQuery();
}

// 更新してみる
// System.Data.SQLliteなら COL_NAME = ?だったけど、それだとダメっぽい。ちゃんと名前をつけてあげて。
SqliteCommand update = connection.CreateCommand();
update.CommandText = "良さげなUPDATE文 SET COL_NAME = $arg1;";
update.Parameters.AddWithValue("$arg1", "hogehoge" + DateTime.Now.ToString());
update.ExecuteNonQuery();

// 読んでみる
SqliteCommand select = connection.CreateCommand();
select.CommandText = "SELECT * FROM 作ったテーブル名;";
using (var reader = select.ExecuteReader())
{
    while (reader.Read())
    {
        for (int i = 0; i < reader.FieldCount; i++)
        {
            Console.WriteLine("read filed[" + i.ToString() + "] = " + (reader.IsDBNull(i) ? "null" : reader.GetString(i)));
        }
    }
}

既存のSystem.Data.SQLite使用コードがある場合は、Microsoft.Data.SQLiteに差し替えてください。
見ての通り、クラス名の大文字小文字が地味に違うので、置換が必要です。

暗号化は続けたいんやが?

一度、Microsoft.Data.SQLiteのみをアンインストールします。
続けて、Microsoft.Data.Sqlite.Coreと、SQLitePCLRaw.bundle_e_sqlcipherをインストールします。

今回はSQLitePCLRaw.lib.e_sqlcipher.2.1.4がインストールされたと仮定して話を進めます。

このままだと暗号化に切り替わっていません。
packages\SQLitePCLRaw.lib.e_sqlcipher.2.1.4\runtimeフォルダから、実行環境に応じたフォルダを選択して、中にあるe_sqlcipher.dllを実行フォルダにコピーします。
面倒だったら、実行ファイルのプロジェクトのビルド後イベントに、runtimeフォルダ含めて一式コピーしても良いです。

xcopy /s /r /c /y $(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlcipher.2.1.4 $(TargetDir)

まぁ、乱暴なので、ちゃんと必要なものだけコピーしましょう。
Windowsシリーズ64bitで動かすつもりならこう。

xcopy /y $(SolutionDir)\packages\SQLitePCLRaw.lib.e_sqlcipher.2.1.4\runtimes\win-x64\native\e_sqlcipher.dll $(TargetDir)

さて、これでパスつきで動くはずです。
ただ、今まで使ってたDBとは暗号方式が違うのか、PupSQLite.exeを使っても開けなくなります
VBA + vbRichClientも使ってアクセスしてたけど、これも使えなくなるのかー

まぁ、私は使う度にDBを作成する系システムを作っているので、無問題としましょう。
困る人はコンバーターでも作ってください。

// この1文も忘れずに
SQLitePCL.Batteries.Init();

// 前述のコードでOpen前にパスワードを指定する
SqliteConnection connection = new SqliteConnection();
string path = @"なんか良さげな場所\test.db";
bool isCreate = !File.Exists(path);

// Open
connection.ConnectionString = new SqliteConnectionStringBuilder("Data Source=" + path)
{
    Mode = SqliteOpenMode.ReadWriteCreate,
    Password = "つけたいパスワード"
}.ToString();
connection.Open();

// 以下同文なので省略

COM登録したDLLとして動きたいんやが?

さて、exeファイルで動作確認もした。
できたなーと思ってインストーラーで配布したところ、dll単体では動きません。
app.Configに書いてあるassemblyBindingタグの効果が得られないため、SqliteConnectionインスタンス生成時にエラーになります。
実装するクラスのコンストラクタで、リダイレクトするための仕組みをいれておきます。
説明をわけました。

DataAdapterを使いたいんやが?

System.Data.SQLiteを手放さなければ使えるっぽい。
ちなみに、INTERGER型のカラムをintでキャストすると吹っ飛ぶので、longでキャストするか、一度ToString()で文字列にしてint.Parse()でパースしてください。

他の方法を紹介している方もいらっしゃいました。
が、それはINTEGER型のカラム名がわかってないといけなさそう。
共通化に向いてないので、試していません。

// OpenやDB用意は前述の通りなので省略

// DataSetで読んで見る
System.Data.Common.DbCommand select2 = connection.CreateCommand();
select2.CommandText = "SELECT * FROM テーブル名;";
select2.CommandType = System.Data.CommandType.Text;
select2.Connection = connection;

System.Data.Common.DbProviderFactory factory = System.Data.Common.DbProviderFactories.GetFactory("System.Data.SQLite");
System.Data.Common.DbDataAdapter adapter = factory.CreateDataAdapter();
adapter.SelectCommand = select2;
System.Data.DataSet dataset = new System.Data.DataSet();
adapter.Fill(dataset);
foreach (System.Data.DataTable table in dataset.Tables)
{
    foreach (System.Data.DataRow row in table.Rows)
    {
        object[] col = row.ItemArray;
        for (int i = 0; i < col.Length; i++)
        {
            Console.WriteLine("read filed[" + i.ToString() + "] = " + (col[i] == null ? "null" : col[i].ToString()));
        }
    }
}

DbProviderFactories.GetFactory()でエラーになる場合は、app.configに下記を追加してください。

<system.data>
<DbProviderFactories>
  <remove invariant="System.Data.SQLite.EF6" />
  <add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
  <remove invariant="System.Data.SQLite" />
  <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>

COM登録したDLLとして動きたいんやが?

だからapp.configは使いたくないんだってば!
System.Data.SQLiteで空&パスワードなしのDBを作って、それを経由してAdapterを作ってみます。

/* これはapp.configを使う必要が出ちゃうので却下
System.Data.Common.DbProviderFactory factory = System.Data.Common.DbProviderFactories.GetFactory("System.Data.SQLite");
System.Data.Common.DbDataAdapter adapter = factory.CreateDataAdapter();
*/

/* System.Data.SQでパスなしの空DBを作ってAdapterを生成する */
string pathForAdapter = @"良さげな場所\testForAdapter.db";
SQLiteConnection con = new SQLiteConnection("Data Source=" + pathForAdapter);
con.Open();
System.Data.Common.DbDataAdapter adapter = new SQLiteDataAdapter(select2.CommandText, con);

動いたわ…いいの? これで大丈夫なの??
さっくり検証しかしていないので、責任は持ちません。


他にもニッチなIT関連要素をまとめていますので、よければ一覧記事もご覧ください。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)