読者です 読者をやめる 読者になる 読者になる

【C#】System.Data.SQLiteを使ってみる

このTumblrの更新は自作のTumblr更新ツールから行われているんですが、既に書いた記事のタグを取得して候補に出す、なんてことをしています。

が、TumblrAPIにはタグ一覧を取得するものがないので、とりあえずHTTP飛ばして記事を取ってくるだけ取ってきてタグだけ抽出と言う割とどうしようもない処理をする必要があります。

流石にもうちょっとなんかあるだろう、と考えたところ、普通にDBに突っ込んでおけばいいんじゃないの?と思いつき、折角だからSystem.Data.SQLiteを使ってみようかなと。

System.Data.SQLiteの導入

普通にダウンロードしてもいいんですが、正直何がなんやらわからないほど大量のバージョンがあって考えるのが面倒なのでNugetからぱぱっと導入するのがいいと思います。

リファレンスどこにあんねんと思ったらヘルプファイル形式のをダウンロードするしかないっぽいです。トップページの一番下にある「downloaded directly」ってところから落とせます。

正直これを読まないと何がなんだかわからないんですが、基本的にはSystem.Data名前空間でやらなきゃいけないことをやれば大丈夫そうです。

コード

当然LINQ to SQLを使えるのでガンガン使っていきます。が、何となくデータプロバイダの動きに癖があると言うか、妙な動きをすることがあると言うか、そもそもSQLite用のデータプロバイダを読み込めてるの…?みたいなとこがあるので、何となく信用なりません。

まぁとりあえず動いたソースです。まずはエンティティ。

using System;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Text;

namespace TumblingDice
{
    [Table(Name="TUMBLR_DATA")]
    public class TumblrData
    {
        [Column(Name = "ID", IsPrimaryKey = true, DbType = "INT")]
        public long ID { get; set; }

        [Column(Name = "TITLE", CanBeNull = false, DbType = "TEXT")]
        public string Title { get; set; }

        [Column(Name = "BODY", DbType = "TEXT")]
        public string Body { get; set; }

        [Column(Name = "URL", DbType = "TEXT")]
        public string Url { get; set; }

        [Column(Name = "TAGS", DbType = "TEXT")]
        public string Tags { get; set; }

        public static string CreateCommand()
        {
            var type = typeof(TumblrData);
            var tableName = (Attribute.GetCustomAttribute(type, typeof(TableAttribute)) as TableAttribute).Name;
            var sb = new StringBuilder("CREATE TABLE IF NOT EXISTS ");
            sb.Append(tableName).Append("(");

            var attrs = type.GetProperties()
                            .Select(x => Attribute.GetCustomAttribute(x, typeof(ColumnAttribute)) as ColumnAttribute);
            foreach (var fieldAttr in attrs)
            {
                var columnName = fieldAttr.Name;
                var dbType = fieldAttr.DbType;
                sb.AppendFormat("{0} {1}", columnName, dbType);

                if (!fieldAttr.CanBeNull)
                {
                    sb.Append(" NOT NULL");
                }

                sb.Append(",");
            }

            sb.Remove(sb.Length - 1, 1);

            if (attrs.Any(x => x.IsPrimaryKey))
            {
                sb.Append(", PRIMARY KEY(");
                foreach (var primaryKey in attrs.Where(x => x.IsPrimaryKey))
                {
                    sb.Append(primaryKey.Name).Append(",");
                }

                sb.Remove(sb.Length - 1, 1);
                sb.Append(")");
            }

            sb.Append(");");

            return sb.ToString();
        }

        public static string GetTableName()
        {
            return (Attribute.GetCustomAttribute(typeof(TumblrData), typeof(TableAttribute)) as TableAttribute).Name;
        }
    }
}

なんてことはない、LINQ to SQL用のAttributeを設定したり、とりあえずCREATE文を作れるメソッドが用意されているだけです。

で、肝心の呼び出し方。

using System;
using System.Data.SQLite;
using System.Data.Linq;
using System.Windows;

namespace TumblingDice
{
    class SqliteTest
    {
        public static void test()
        {
            var connectionString = new SQLiteConnectionStringBuilder
            {
                DataSource = "test.db",
            };

            using (var con = new SQLiteConnection(connectionString.ToString()))
            {
                try
                {
                    using (var command = new SQLiteCommand(con.OpenAndReturn()))
                    {
                        command.CommandText = TumblrData.CreateCommand();
                        command.ExecuteNonQuery();
                    }

                    var context = new DataContext(con);
                    var tumblrs = context.GetTable<TumblrData>();

                    tumblrs.InsertOnSubmit(new TumblrData { ID = 0, Title = "test" });
                    context.SubmitChanges();

                    foreach (var tumblr in tumblrs)
                    {
                        Console.WriteLine(tumblr.Title);
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show(e.Message);
                }
            }
        }
    }
}

これまた見ての通り、Connection作って、Command投げて、DataContextを作って、適当にデータを操作していく、いつものSystem.Data名前空間の操作です。

DataSourceのDB名はなければ勝手に作ってくれます。特にパスを指定しなければ、exeと同じディレクトリ内を探しにいくみたいですね。

最初TumblrData.IDのDbTypeをINTEGERと記述していたんですが、GetTableするところで「そんな型はない」と一蹴されてしまいました。

内部的に何をやってるのかアレなんですが、私はこの程度のコードが動くならそれでいいので気にしないことにします。

まとめ

一番ハマった箇所はドキュメントの在り処でした。

System.Data名前空間と言う.NET最大の失敗作をバリバリ使ってる人は、ちょっと適当なDBが欲しい時にSQLiteを検討してみてはいかがでしょうか。