5章はやたらと問題数が多かった。 ちなみに5.16は間違っている。
SQLiteにDapperで読み書きする
C#でSQLite読み書きするときに使うORMを探していたところ、Dapper.NetというORMを見つけた。
このDapper.Netがとても使いやすかったので紹介がてら、使い方のメモを残そうかと思う。
事前準備
- Nugetから「Depper dot net」をインストールする
Nugetから「System.Data.SQLite」をインストールする
以下のような「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(); }
ちゃんとデータが取得できた。
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への参照の追加
プロジェクトの参照設定を右クリックして「参照の追加」を選択します。
参照マネージャーのウィンドウが表示されるので、フレームワークから「System.Windows.Forms」にチェックを付けます。
WindowsFormsIntegration.dllへの参照追加
参照マネージャーの参照ボタンを押下します
ファイル選択ダイアログが表示されるので「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表示されることが確認できました。
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 XでVagrant上の仮想マシン(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
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」
疎通確認用プログラムの作成
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