SQLiteにDapperで読み書きする

C#SQLite読み書きするときに使うORMを探していたところ、Dapper.NetというORMを見つけた。

github.com

このDapper.Netがとても使いやすかったので紹介がてら、使い方のメモを残そうかと思う。

事前準備

  1. Nugetから「Depper dot net」をインストールする
  2. Nugetから「System.Data.SQLite」をインストールする

  3. 以下のような「user」テーブルを作成します。

CREATE TABLE user(
  id integer primary key,
  name text,
  age integer
);

SELECT文

とりあえずまずはSELECTができる事を確かめてみる。
先ほど定義したuserテーブルに適当なデータを3件ほど挿入しておく。

上記のUserテーブルを格納するクラスを作成する。
プロパティ名称はテーブル定義のカラム名称と合わせ、integer型の主キーはlong?で定義する。

public class User
{
    public long? ID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

usingを追加する

using Dapper;

SELECT文を投げる処理を追加する。

public static IEnumerable<User> GetAll()
{
    var config = new SQLiteConnectionStringBuilder() 
    {
        DataSource = @"..\..\..\testdb.db" 
    };
    using (var connection = new SQLiteConnection(config.ToString()))
    {
        connection.Open();
        return connection.Query<User>(@"select * from user");
    }
}

プロパティメンバーを表示するためのToStringを追加する。

public override string ToString()
{
    return string.Format("ID={0}, Name={1}, Age={2}", ID, Name, Age);
}

SELECTしたデータを表示してみる

static void Main(string[] args)
{
    User.GetAll().ForEach(x => Console.WriteLine(x));

    Console.ReadKey();
}

ちゃんとデータが取得できた。

f:id:hkou:20150719082404p:plain

INSERT文

SELECT処理と被るので、コネクションを返す部分をメソッドに分割する

private static SQLiteConnection GetConnection()
{
    var config = new SQLiteConnectionStringBuilder()
    {
        DataSource = @"..\..\..\testdb.db"
    };
    return new SQLiteConnection(config.ToString());
}

Insert処理
値は「@名前」形式で指定する。

public static void Insert(User user)
{
    using (var connection = GetConnection())
    {
        connection.Open();
        using (var tran = connection.BeginTransaction())
        {
            try { 
                connection.Execute("insert into user(name, age) values(@name, @age)", user, tran);
                tran.Commit();
            }
            catch (Exception)
            {
                tran.Rollback();
            }
        }
    }
}

UPDATE文

UPDATE文もInsert文と同じくExecuteメソッドを呼び出して実行する。

public static void UpdateById(User user)
{
    using (var connection = GetConnection())
    {
        connection.Open();
        using (var tran = connection.BeginTransaction())
        {
            try
            {
                connection.Execute("update user set name=@name, age=@age where id=@id", user, tran);
                tran.Commit();
            }
            catch (Exception)
            {
                tran.Rollback();
            }
        }
    }
}

WPFアプリケーションからWinFormsのコントローラを使う

WPFアプリケーションを作成していると、WPFにはないけど、WinFormsにはあるコントロールをどうしても使いたいという場面が出てきます。今回はそんなときにWPFアプリケーションからWinFormsコントローラを使用する方法を解説します。

System.Windows.Formsへの参照の追加

プロジェクトの参照設定を右クリックして「参照の追加」を選択します。

f:id:hkou:20150630215430p:plain

参照マネージャーのウィンドウが表示されるので、フレームワークから「System.Windows.Forms」にチェックを付けます。 f:id:hkou:20150630215543p:plain

WindowsFormsIntegration.dllへの参照追加

参照マネージャーの参照ボタンを押下します f:id:hkou:20150630215950p:plain

ファイル選択ダイアログが表示されるので「WindowsFormsIntegration.dll」を選択します。

※このDLLは.Netのインストール先フォルダにあります。

例:C:\Windows\Microsoft.NET\Framework64\v4.0.30319\WPF

XAMLの修正

名前空間の追加

WinFormsコントロールを参照するために名前空間を追加します

<Window x:Class="UsingWinFormsTest.Views.MainWindow"

   xmlns:winform="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
   …>

コントロールの追加

今回はWinFormsのLabelを使用します

<Grid>
    <WindowsFormsHost>
        <winform:Label Text="Windows Forms Label" />
    </WindowsFormsHost>
</Grid>

実行

実行してみます。ちゃんとLabel表示されることが確認できました。

f:id:hkou:20150630220614p:plain

ViewModelから他のViewを開く

概要

ViewModelから他のViewを開きたいとき、WinFormの場合だとFormのインスタンスを生成してShowDialog()を呼び出していたと思うが、MVVMパターンに準拠するとViewModelからViewに依存することになってしまう為、その方法は使用できない。

ではどうするかというとMessageパターンというものを使用して、ViewModel→Message→ViewというようにMessageを介すことでViewに依存関係を作らないようにするのだ。
これを普通に書くと結構なコード量になるが、LivetにはちゃんとMessageパターン用のクラスが用意されている。

やりたいこと

MainWindowからSubWindowを表示する

MainWindowViewModel.cs

MainWindowのViewModelからSubWindow表示用のメッセージを送る。
TransitionMessageコンストラクタの第一引数には表示するWindowのViewModelを渡し、第2引数にはメッセージキーを渡す。
メッセージキーはメッセージを受け取った時の振り分けに使用される。

using Livet;
using Livet.Messaging;

public void ShowSubWindow()
{
    Messenger.Raise(new TransitionMessage(new SubWindowViewModel(), "SubViewMessageKey"));
}

MainWindow.xaml

xamlにはメッセージを受け取り、SubViewを表示する旨を記述する。

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"

<i:Interaction.Triggers>
    <l:InteractionMessageTrigger MessageKey="SubViewMessageKey" Messenger="{Binding Messenger}">
        <l:TransitionInteractionMessageAction WindowType="{x:Type v:SubWindow}" Mode="Modal"/>
    </l:InteractionMessageTrigger>
</i:Interaction.Triggers>

RabbitMQのインストールと疎通確認まで

概要

Mac OS XVagrant上の仮想マシン(CentOS7)にRabbitMQをインストール
Mac上のScalaプログラムからRabbitMQにアクセス

Vagrant

CentOS7のminimal boxをベースに環境構築していく

vagrant box add CentOS7 https://github.com/holms/vagrant-centos7-box/releases/download/7.1.1503.001/CentOS-7.1.1503-x86_64-netboot.box
vagrant init CentOS7

Vagrantfileが作成されたと思うので、以下のRabbitMQ用のポートフォワーディング設定を追加する

vi Vagrantfile 
↓追加↓
config.vm.network :forwarded_port, guest: 5672, host: 5672
config.vm.network :forwarded_port, guest: 15672, host: 15672

初期メモリが500MBぐらいしか割り当てられないので、それでは心もとないからメモリ1GB割り当てられるように設定する。

config.vm.provider "virtualbox" do |vb|
     vb.customize ["modifyvm", :id, "--memory", "1024"]
end

追加したら仮想マシンを起動して、SSHする

vagrant up
vagrant ssh

CentOS7の事前準備

とりあえずSELinuxは無効化

setenforce 0

FirewalldのRabbitMQ用のポートを解放しておく

firewall-cmd --permanent --add-port=5672/tcp
firewall-cmd --permanent --add-port=15672/tcp

設定を反映する

firewall-cmd --reload

RabbitMQのインストール

Erlangと別々にインストールするのがだるいのでEPELレポジトリのRabbitMQを使用する。

sudo yum install https://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
sudo yum -y install rabbitmq-server

Erlangがインストールされたか確認

[vagrant@localhost ~]$ erl
Erlang R16B03-1 (erts-5.10.4) [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]

Eshell V5.10.4  (abort with ^G)
1> erlang:system_info(otp_release).
"R16B03-1"
2> q().
ok

RabbitMQがインストールされたか確認

[vagrant@localhost ~]$ sudo rabbitmqctl status | grep rabbit,
      {rabbit,"RabbitMQ","3.3.5"},

RabbitMQのManagement Pluginを有効化する。
Web画面ベースでQueueの管理とか出来るので絶対いれるべき

sudo rabbitmq-plugins enable rabbitmq_management

Config作成

cd $RABBITMQ_HOME/etc/rabbitmq/
vi rabbitmq.config 

↓以下の行を追加
[{rabbit, [{loopback_users, []}]}].

設定を反映するためにRabbitMQを再起動

sudo service rabbitmq-server restart

管理画面表示できるか確認

curl http://127.0.0.1:15672/

Macでも表示できるか確認 初期IDとパスワードはどちらも「guest」 f:id:hkou:20150614134653p:plain

疎通確認用プログラムの作成

RabbitMQ tutorialsの「Hello World」をScalaで作成する

RabbitMQ - RabbitMQ tutorial - "Hello World!"

build.sbtに以下を追加

libraryDependencies += "com.rabbitmq" % "amqp-client" % "3.5.3"

Producer.scala(送信)

class Producer(queueName : String) {
  val factory = new ConnectionFactory()
  factory.setHost("localhost")
  val connection = factory.newConnection()
  val channel = connection.createChannel()

  channel.queueDeclare(queueName, false, false, false, null)

  def send():Unit={
    var message = "Hello World!"
    channel.basicPublish("", queueName, null, message.getBytes())
    println(" [x] Sent '" + message + "'")
  }

  def close():Unit={
    channel.close()
    connection.close()
  }
}

Consumer.scala(受信)

class Consumer(queueName : String) {
  val factory = new ConnectionFactory()
  factory.setHost("localhost")
  val connection = factory.newConnection()
  val channel = connection.createChannel()

  val consumer = new QueueingConsumer(channel)
  channel.basicConsume(queueName, true, consumer)

  def receive():Unit={
    val delivery = consumer.nextDelivery()
    val message = new String(delivery.getBody())
    println(" [x] Received '" + message + "'")
  }

  def close():Unit={
    channel.close()
    connection.close()
  }
}

Main.scala

object Main {
  val QUEUE_NAME="test_queue"

  def main(args: Array[String]) {

    val p = new Producer(QUEUE_NAME)
    val c = new Consumer(QUEUE_NAME)

    p.send()
    c.receive()

    p.close()
    c.close()
  }
}

実行結果

 [x] Sent 'Hello World!'
 [x] Received 'Hello World!'

プロジェクトのコードはGitHubに上げました github.com

MSIインストーラをユーザー権限で実行出来るようにする

やりたいこと

普通にMSIインストーラを作ると、特に管理者権限が必要なことしてないのに、毎回管理者権限を求められるUACが表示される(※)。これを消したい
Windows Installer version 4.0からの仕様らしい

方法

PlatformSDKのmsiinfoを使用する。 以下を実行すると指定したmsiファイルをElevated privilegesを要求しない設定に書き換えてくれる。

MSIファイルを圧縮設定している場合

msiinfo target.msi /W 10

MSIファイルを無圧縮設定している場合

msiinfo target.msi /W 8

参考にしたページ

Msiinfo.exe (Windows)