write ahead log

ロールフォワード用

Visual Studio Installer Projectで上書きインストールができない

インストーラを作りたい

昔々, Visual Studio にはInstaller Projectというインストーラを作成するためのプロジェクトを作成する機能がありました.

機能が少なくても標準でついてきていたので非常に便利だったようなのですが2005くらいから消えてしまったそうです.

(伝聞なのは私自身使ったことがないためです)

しかし未だにVS2017が出てもなお, 私はWindowsフォームアプリケーションをせっせと作るお仕事をしているので, インストーラが標準で作れないのは非常に不便です.

と思ったらVS2015からアドオンとして復活しています.

Visual Studio: Marketplace - Microsoft Visual Studio 2015 Installer Projects

具体的な使い方は他サイトにたくさんあるので参照してみてください.

おすすめです.

Microsoft Visual Studio 2015 Installer Projects をインストールする (Visual Studioの使い方 Tips)

Microsoft Visual Studio 2015 Installer Projects を利用してインストーラーを作成する

バージョンアップインストールが出来ない

上記で作れたのは良いのですが, インストール済みのプログラムを更新する方法がわからなくて困っていました.

解決したのでメモしておきます.

因みにこの問題, 海外でも結構困っている人が多いようです.

Visual Studio forum - Setup project does not uninstall previous version

解決法

スクリーンショットを取るのが面倒なのでテキストでだけ.

重要なのは以下の3つです.

  • インストーラプロジェクトのプロパティウィンドウで「RemovePreviousVersions」をtrueに設定する
  • リリースの度にインストーラプロジェクトのプロパティウィンドウで「Version」を向上させてやる
  • 入れ替えるDLLやexeのプロジェクトのプロパティウィンドウで「ファイルバージョン」を向上させてやる

最後のがハマりました.

アセンブリバージョンを一生懸命変えていたのですが, どうもインストーラプロジェクトが見るのは「ファイルバージョン」の様です.

アセンブリバージョンはビルドの度に簡単に番号を向上させる方法もあったりするのですが, ファイルバージョンはなさそうなので中々面倒です.

とはいえ, 目的は達せられたのでよしとします.

CentOS7上にApache + PassengerでSinatraの動く環境を作る

Rubyを入れる(rbenvの導入)

rbenvで入れます.

rootへrbenvを入れますが, まずは環境を整えます.

$ sudo yum install git gcc gcc-c++ openssl-devel readline-devel

rbenvのリポジトリをclone. システム全体へ導入するので/usr/localへ入れます.

$ sudo git clone https://github.com/sstephenson/rbenv.git /usr/local/rbenv

rootへ昇格します.

$ su -

rbenv.shを作成します.

# vi /etc/profile.d/rbenv.sh
# 以下を記述
  export RBENV_ROOT="/usr/local/rbenv"
  export PATH="${RBENV_ROOT}/bin:${PATH}"
  eval "$(rbenv init --no-rehash -)"

設定ファイルを読み直します.

# source /etc/profile.d/rbenv.sh

ruby-buildを導入していきます.

# mkdir -p /usr/local/rbenv/plugins
# cd /usr/local/rbenv/plugins
# git clone https://github.com/sstephenson/ruby-build.git ./ruby-build
# cd ./ruby-build/
# ./install.sh

ようやくrubyの導入.

まずはrbenvで入れられるrubyのリストを表示します.

# rbenv install -l

今回は2.5.0にします.

# rbenv install 2.5.0
# rbenv global 2.5.0

最後にバージョンを確認.

ruby -v

SELinuxを無効にする

ごめんなさいごめんなさいごめんなさい.

# vi /etc/selinux/config
# 以下を変更
#SELINUX=enforcing
SELINUX=disabled

Passengerを入れる

gemでとりあえず投入.

# gem install passenger

続いてapacheモジュールの方も入れる.

# passenger-install-apache2-module

TUIが表示されるので, 1 => Ruby でEnter

一度abortします.

不足しているライブラリが表示されるので(親切!)インストールします.

# yum -y install libcurl-devel httpd httpd-devel apr-devel apr-util-devel

再度挑戦.

# passenger-install-apache2-module

入りました.

apacheの設定のスニペットが表示されます.

見逃しても以下で再表示できます.

# passenger-install-apache2-module --snippet
LoadModule passenger_module /usr/local/rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/passenger-5.2.1/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
  PassengerRoot /usr/local/rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/passenger-5.2.1
  PassengerDefaultRuby /usr/local/rbenv/versions/2.5.0/bin/ruby
</IfModule>

/etc/htpd/conf.dの下にpassenger.confを作ります. 上記のスニペットをコピペします.

# passenger-install-apache2-module --snippetの内容
LoadModule passenger_module /usr/local/rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/passenger-5.2.1/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
  PassengerRoot /usr/local/rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/passenger-5.2.1
  PassengerDefaultRuby /usr/local/rbenv/versions/2.5.0/bin/ruby
</IfModule>

# これはついで
# Passengerが追加するHTTPヘッダを削除する
Header always unset "X-Powered-By"
Header always unset "X-Rack-Cache"
Header always unset "X-Content-Digest"
Header always unset "X-Runtime"

# virtualhostの設定
<VirtualHost *:80>
  # ドメイン
  ServerName ドメイン名
  # アプリのルート
  DocumentRoot "/var/www/apps/sample_app/public"
  RackEnv production
</VirtualHost>

設定ファイルをチェック.

# apachectl configtest
AH00112: Warning: DocumentRoot [/var/www/apps/sample_app/public] does not exist
Syntax OK

apacheを再起動

# apachectl restart

アプリの設定

とりあえずbundlerを入れます.

# gem install bundler

passenger.confで指定したディレクトリを作ります.

tmpはpassengerのリロードに使います.

# mkdir -p /var/www/apps/sample_app/public
# mkdir -p /var/www/apps/sample_app/tmp

アプリのディレクトリへ移動して以下の様にアプリを作っておきます.

# cd /var/www/apps/sample_app

アプリ本体.

# app.rb
require 'sinatra'

get '/' do
  'Hello World!'
end

rackupファイル.

# config.ru
require File.expand_path(File.dirname(__FILE__)) + '/app'
run Sinatra::Application

Gemfile

source "https://rubygems.org"

gem 'sinatra'

最終的にディレクトリ構成はこんな感じ.

# pwd
/var/www
# tree .
.
├ apps
│   └ sample_app
│       ├ app.rb
│       ├ config.ru
│       ├ Gemfile
│       ├ Gemfile.lock
│       ├ public
│       └ tmp
├ cgi-bin
└ html

bundleを実行

# bundle install

ブラウザでアクセスすれば挨拶されます.

いやー, これ長い.

Sinatraを使う

久しぶりにRubyを触っているこの頃ですが, Railsはやはり覚える事が多いのでまだまだSinatraの方が私には楽です.

遠い記憶になっているので少しまとめました.

基本的にClassicスタイルで書いていきます.

書いてて思ったのは大体は公式に記載があるということです.

基本

ルーティング

普通のルーティング

HTTPのメソッド名 + ルート + BlockというSinatraが有名になった美しい構文.

Rubyらしく最後に評価された式の値でレスポンスになります.

get '/' do
  'ここは/にgetリクエストを送ったときに呼び出される場所'
end

post '/' do
  ...
end

put '/' do
  ...
end

delete '/' do
  ...
end

URLパラメータでのルーティング

URL内の

:変数名

がパラメータになります.

require 'sinatra'

get '/:hoge' do      #=> localhost:4567/fooへアクセス  -> fooと表示
  params[:hoge]
end

公式を読むと他にも条件付きマッチなどかなり色々できます.

リダイレクト

URL指定
get '/' do
  redirect 'https://www.google.com'
end
パス指定
get '/' do
  redirect to('/topath')
end
元いた場所へ戻る
get '/back' do
  redirect back
end

変わり種

正規表現でのルーティングやhalt/passというメソッドが面白いです.

halt

haltは途中で処理を止めてレスポンスを返します.

以下の例ではクエリパラメータhiにhiが渡された時だけhiと表示します.

require 'sinatra'

get '/' do
  halt "hi" if params['hi'] == "hi"
  "Hello"
end

他にもステータスコードを渡したりもできるようです.

正規表現でのルーティングとpass

ルーティングのURLには正規表現が使えます.

また, passメソッドを使うと他のマッチするルートへ移ります.

以下の例ではgreeting以下のhiにアクセスする時以外は全て2番目のルートで処理しますが, greeting/hiへのアクセス時にクエリパラメータでhi=hiが設定されているときはやはり2番目のルートで処理します.

(例が悪かったかな)

require 'sinatra'

get '/greeting/hi' do
  pass if params['hi'] == 'hi'
  "Hi"
end

get '/greeting/*' do
  "Hello"
end

パラメータ(params)

GetパラメータもPostパラメータもURLパラメータも以下だけでOKです.

params[:パラメータ名のシンボル]

ルーティングのブロックから取ったりもできるようですが.

外部テンプレート(Views)

viewsディレクトリを作っておくといい感じにしてくれます.
(erbの例)

.
├── server.rb
└── views
    └── index.erb

Rubyコードからはerbメソッドで拡張子抜きのファイル名のシンボルを呼び出すとviews以下のerbが呼ばれます.

require 'sinatra'

get '/' do
  erb :index
end

erbはこんな感じで普通に書きます.

<h1>Hello</h1>

<%= DateTime.now.strftime('%D %X') %>

共通レイアウト

共通レイアウトをまとめるlayoutも使えます.

viewsディレクトリの中にlayout.erbで配置するだけです.

.
├── server.rb
└── views
    ├── index.erb
    └── layout.erb

layout.erbの中ではyieldで個々のテンプレートへ移譲します.

<!DOCTYPE html>
<html>
    <head>
       <title>layout Sample</title>
   </head>
    <body>
        <%= yield %>
    </body>
</html>

Rubyのコードでテンプレートを呼び出すときにlayoutの有無を選択できるようです.

get '/' do
  erb :index, layout: false
end

JSONを返す

content_typeを明示的に指定する必要がある.

require 'sinatra'
require 'json'

get '/' do
  content_type :json
  data = { message: 'msg' }
  data.to_json
end

ヘルパーメソッド

ビューで使いたいメソッドはhelpersメソッドのブロックに書いておくとテンプレート内から呼び出せるようになります.

require 'sinatra'

get '/' do
  erb :index
end

helpers do
  def help
    "Help!"
  end
end

HTMLはこんな感じ.

<%= help %>

これで"Help!"と表示されます.

静的ファイル配信

デフォルトでpublicディレクトリが公開されます.

こんな配置にしておけば

.
├── public
│   └── css
│       └── style.css
├── server.rb
└── views
    └── index.erb

HTMLからはこれで呼び出せます.

<html>
    <head>
       <link rel="stylesheet" href="css/style.css">
   </head>
    <body>
    Link style
    </body>
</html>

ファイルアップロード

params[:fileのinput名]にファイルに関する情報が入っているのでこれを使います.

params[:fileのinput名][:filename] # ファイル名
params[:fileのinput名][:tempfile] # ファイルそのもの

Rubyのコードはこんな感じ.

require 'sinatra'

get '/' do
  erb :index
end

post '/upload' do
  if params[:file1]
    # カレントのimagesディレクトリへ保存する
    path = "images/#{params[:file1][:filename]}"
    File.open(path, 'wb') { |f|
      f.write params[:file1][:tempfile].read
    }
  end
  redirect back
end

indexのアップロードフォームも適当に.
enctype指定は忘れがち.

<form action="/upload" method="POST" enctype="multipart/form-data">
    <input type="file" name="file1">
    <input type="submit">
</form>

ファイルダウンロード

send_fileにカレントからのファイルパスを書けば送信できます.

typeでmimeも指定できる.

get '/' do
  send_file 'images/img.jpg', type: :jpg
end

フィルタ

beforeとafterメソッドがあります. そのままですね.

before do
  ...
end

after do
  ...
end

条件付きマッチもできます.

before '/loggerarea/*' do
  logger.info "logging"
end

DB接続

Railsの様に永続化機能がフレームワークで用意されているわけではないので, 自由に選べます.

ActiveRecordの例が多いですが, 個人的にはそのレベルならRails使う方が良い気がするのでSequelが簡単かなと思います.

セキュリティ系

この話題は避けられない割にキリがないのですが, とりあえず最低限だけ.

XSS対策

helperにRackのescape_htmlの呼び出しを書いておくとviewでも使えるようになります.

helpers do
  def h(text)
    Rack::Utils.escape_html(text)
  end
end

erbなら以下.

<%= h "<script>alert()</script>" %>

Sessionを使う

戸惑うことはないでしょう.

# 有効化
enable :sessions

# 記録
session[:message] = "This line use session"

# 読出
session[:message]

CSRF対策

Rack::ProtectionへSinatra本体が依存しているので, 既に利用がマージされてるようです.

コードの先頭で呼び出せばさらに色々使えます.

# server.rb
require 'sinatra'
require 'rack/protection'   #ここ

use Rack::Protection        #ここ
enable :sessions

全機能使おうとするとsessionが有効でないと使えないので注意.

Basic認証

StackOverFlowまんまですが.

  def authorized?
    @auth ||=  Rack::Auth::Basic::Request.new(request.env)
    @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ["ユーザ名","パスワード"]
  end

  def protected!
    unless authorized?
      response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
      throw(:halt, [401, "認証が必要です\n"])
    end
  end

これで以下のように使えます.

get '/' do
  "認証なし"
end

get '/auth' do
  protected!
  "認証あり"
end

古いですが, Sinatra RecipesにはDigest認証の方法についても記述がありました.

エラーページ

not foundはその名の通りnot_foundメソッドでキャッチできます.

他のエラーはerrorメソッドでキャッチできます.

errorメソッドは開発環境ではデバッグの表示が出るので, プロダクションに設定してやると分かりやすいです.

require 'sinatra'

configure do
  set :environment, :production
end

get '/' do
  "Hello"
  raise "not found"
end

not_found do
  "not found"
end

error do
  "sorry"
end

ちなみにREADMEを読むとerrorは引数もとれるらしいです.

ロギング

普通にRubyのloggerです.

get '/' do
  logger.info 'access to root'
end

Sinatra(というかRack?)がデフォルトで用意している出力先はenv['rack.logger']の様.

プロダクションではアプリケーションサーバが担当するのか?

テスト

Classicスタイルの場合. Modulerスタイルの場合はappのアプリケーションを変えてやればよいそうで.

require './app.rb'
require 'test/unit'
require 'rack/test'

class MyAppTest < Test::Unit::TestCase
  include Rack::Test::Methods

  def app
    Sinatra::Application
  end

  def test_root
    get '/'
    assert last_response.ok?
    assert_equal last_response.body, "Hello"
  end
end

デプロイ

長くなるので別記事にしよう.

便利ライブラリがあります.

GitHub - sinatra/sinatra-contrib

参考

公式

StackOverFlow - Display Sinatra Basic HTTP Auth On One Page Only

Rubyのlogger

UbuntuにrbenvでRubyを入れる

手っ取り早くaptでも入れられるんだけど, かなり古くてMRIだとバージョンが2.0.0のrc版しかない. (14.04.5 LTS, Trustyで試した自分が悪いとは思うけど)

これはいかがなものかということで, GitHubから入れる事にしたが, ハマっちゃったのでメモ.

rbenvを入れる

$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv

ruby-buildを入れる

$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

pathを通す

.profileに書くのかと思ったら違った.

$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

rbenv用にディレクトリを作る

$ sudo mkdir -p /usr/local/rbenv/shims
$ sudo mkdir -p /usr/local/rbenv/versions

.profileを読み直す

$ source .profile

確認する

$ rbenv install -l

インストール可能なRubyの一覧が出る.

インストールしてみる

$ rbenv install 2.5.0

エラーが出た

/tmp/ruby-build.[日付].log

を見ると以下の感じ.

 94% [821/871]  object.c
 94% [822/871]  pack.c
 94% [823/871]  parse.c
Killed
make: *** [rdoc] Error 137

rdocを無視してみる

$ export RUBY_CONFIGURE_OPTS=--disable-install-doc

ビルドできたので使ってみる

$ rbenv local 2.5.0
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

Ubuntu(17.10)上で手っ取り早くRuby + Apache2でCGIを使う

CGIなんて久しく使っていなかったのでメモっておく.

Apache2を入れる.

とりあえずコレ的な.

$ sudo apt install apache2

Rubyも入れる

$ sudo apt install ruby

CGIを有効にする

Debian系の設定は初めてで戸惑った.

$ sudo a2enmod cgid

apacheを再起動.

$ sudo systemctl restart apache2

CGIを有効にする(2)

設定ファイルを変更する.

以下を変更.
(普通は上書きしないと思うが, 手抜き)

/etc/apache2/sites-available/000-default.conf

下記をVirtualHost内に追加する.

ディレクトリは

/var/www/cgi-bin

を使うのが一般的だと思うけど, 諸事情でhomeに作った.

   ScriptAlias /cgi-bin/ "/home/twinbird/cgi-bin/"
    AddHandler cgi-script .cgi .rb
    <Directory "/home/twinbird/cgi-bin">
        AllowOverride None
        Require all granted
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Order allow,deny
        Allow from all
    </Directory>

別にやらなくても良いと思うけど, ServerNameをGlobal設定しておくと警告も消えた.

ここまでやったらapache2を再起動.(またはリロード)

sudo systemctl restart apache2

CGIを実行にする

業界の常識に従い, とりあえず挨拶しておく.

上記設定のディレクトリ(この場合は/home/twinbird/cgi-bin/)へ以下を配置.

#!/usr/bin/ruby
# /home/twinbird/cgi-bin/sample.cgi

require 'cgi'

cgi = CGI.new

puts cgi.header
puts "Hello, world"

権限変更しておく.

$ sudo chmod +x sample.cgi    # 705にするのがよかろう

ブラウザで

localhost/cgi-bin/sample.cgi

へアクセスして挨拶されればOK.

RubyでCGIとか学生以来じゃなかろうか.

cgi.headerをputsしてなくて30分以上ハマったのはココだけの話.

VB.NET(Windows Forms)で見積管理システムを作った

昨今の技術動向をガン無視ですが, ここ2,3ヶ月ほど深夜にぼちぼちやっていたのが形になったので.

github.com

Releaseにインストーラを入れてみました.

TravisCIが使えるかと思いましたが, 流石にWinformは無理でした.(VBはサポートしてるんですね)

目的

一応ある程度は作った目的があって

  • ある程度モジュール化された設計ができるかやってみる
  • テスト可能な設計ができるかやってみる
  • データバインディングスタイルで実装してみる
  • 使ったことの無いコントロールを知る
  • インストーラで入れて動くとこまで1人で作ってみる

お仕事もほとんどがVB.NET + Winformsなんですけどね.

やっぱシステムインテグレーションの業界にいると人売りか業務知識偏重になっちゃって技術面は仕事で伸ばすのは難しいかなと思います.

(面白いとこもありますけどね)

仕事でできないなら, 一度自分でやってみるかと.

作るもの

業務システムっぽいものを作らないと, あんまりリアリティがないです.

ということで, 単純な見積書を作成するシステムを作ってみる事にしました.

見積書はGoogleで適当に検索して出てきたテンプレートをミックスしてフォーマットにしました.

こんな感じです.

UIは参考にするためにVectorでいくつか類似ソフトを落として見てみましたが, 参考になったのかよくわかってないです.

案外, 世の中にあるものは変わらないなという印象でした.

最終的に下記の様なUIにしました.

そんなわけで, 機能的には雑です.

利用ツール

仕事でやるわけじゃないので, 全部タダでないとなりません.(必死)

今のMSにはVisual Studio Communityがあります.

これのおかげで普通に開発ができました.

帳票が問題でしたが, MS Reportがあったので何とかなりました.

主だったライブラリは以下の通りです.

ライブラリ/ミドルウェア 用途
.NET Framework 4.5 これがないとはじまらない
MS Test テストフレームワーク
MS Report 帳票作成
ADO.NET RDBMSアクセス用
SQLite3 RDBMS
MS VS Installer Projects インストーラ作成

Entity Framework使わないの? という感じですが, あえて使っていません.(便利なのはわかります)

O/Rマッパーが嫌いなわけじゃないんですが

  • システム全体の規模が大きくなると最終的に複雑なSQLを発行する事になる
  • みんな横着してO/Rマッパーを使ってパフォーマンスが酷いことになる
  • みんな横着してO/Rマッパー使ったつもりが逆に辛くなる

というケースも見てきているのであえて使わずにおきました.

ただ, 今回の例だとCRUDばかりなので, 明らかに使った方が良いです.
(実戦ではもっと要件が複雑化するでしょうが)

設計

このアプリケーションは8つのプロジェクトで構成されています.

├ADOWrapper
│  ├Interface
│  └SQLite3
├Application
│  ├Component
│  ├Form
│  ├Report
├Domain
│  ├DomainObject
│  └Repository
├Infrastructure
│  ├DDL
│  └RepositoryImpl
├Setup
├TestADOWrapper
├TestDomain
├TestInfrastructure

Testから始まるプロジェクトは各プロジェクトのテストプロジェクトです.

Setupはインストーラ作成のためのプロジェクトです.

各プロジェクトの依存関係は以下の通りです.(Test, Setupプロジェクトを除く)

---------------        --------------- 
| Domain      |        | ADOWrapper  |
---------------        ---------------
   ^        ^            ^
   |        |            |
   |     ------------------
   |     | Infrastructure |
   |     ------------------
   |        ^
   |        |
---------------
| Application |
---------------

ADOWrapperはADO.NETの簡易なラッパーです.

このソフトウェア全体を通して, DBへのアクセスにはこのADOWrapperを用いています.


Infrastructureは永続化のための機能を提供します. これは, DomainプロジェクトのRepositoryインターフェースに対する実装です. RDBMSを差し替える場合にはおそらくInfrastructure全体を差し替える事になります.

(ADOWrapperもInterfaceと実装が分かれているため, 基本的にはRDBMSの差異を吸収することができます. しかし, たいていの業務アプリケーションはRDBMS実装依存のSQLを使わざるおえないでしょう. そうなるとSQLを記述するInfrastractureプロジェクトの差し替えが必要です.)


Domainは業務的に意味のある塊をクラスにしたものの集まりです.

入力の妥当性確認や計算(見積なら合計金額や税率計算)などもこのプロジェクトのクラスで行っています.


Applicationは普通のWindows Form Application(いわゆるWinformsアプリ)です.

画面とのデータのやり取りは極力データバインディングを利用しています.

Domainプロジェクトのデータ構造をそのままバインディングしている箇所もあれば, 間にPresentation(Data Transfer Object)というクラスを中継してバインディングしている所もあります.

単純なマスタの場合など, 直接Domainプロジェクトのクラスをバインディングしている箇所が多いですが, 検索して表示する一覧などは後からドメインオブジェクトを跨いだ項目表示を要求されることが多いという経験上, Presenterを挟むようにしています.

      -----------------
      | Event Handler |
      -----------------
        | Control  |
        |          |
-------------   ------------------
| Presenter |---| Domain Object  |
-------------   ------------------
        |          |
        | Data     |
        | Binding  |
        |          |
      ------------------
      | User Interface |
      ------------------

と, ここまでの話はほとんど本やWebサイトの見よう見まねです.

できる人がやればもっときれいになりそうですが, 今の私にはこれが限界です.

とはいえ, 値オブジェクト化できるところもたくさんあるし, Helperみたいなのを作ってコントロールの初期化なんかはまだまだ共通化できそうです.

帳票や集計関連の処理なんかも追加/複雑化したらServiceを用意すれば対応できそうです.

実装

Winform + データバインディングでのプログラミングをほぼ経験していないので, 良い機会になりました.

データバインディングめっちゃ便利じゃん.


他にはコントロールを勉強しなおす機会になりました.

というのも, 恥ずかしながら今まで下記のコントロールを使ったことがなかったからです.

  • BindingSource
  • ErrorProvider
  • ReportViewer(MS Report)
  • HelpProvider
  • MDI Window

あとは, DataGridViewのカスタムはほぼやった事がなかったので勉強になりました.

自作コンポーネントで知って良かったのは以下です.

VB化してありがたく使いました.

  • 背景色を変えるコンポーネント(何故か絶対に要件になる)
  • DataGridViewにソート機能を付けるためのBindingList
  • DataGridViewCellのカスタムコントロール

ソースはComponent以下です.

参考URL

参考URLが多すぎるのですが, 多少整理して貼っておきます.

設計

TERASOLUNA Server Framework for Java (5.x) Development Guideline - アプリケーションのレイヤ化

いきなり.NETじゃない.

NTTデータのフレームワークの設計規約みたいです.

なんだかんだ参考になりました.

GUIアーキテクチャパターンの基礎からMVVMパターンへ

ふむー.って感じ.

Microsoft - .NET におけるアプリケーション アーキテクチャ ガイド

このリストの中で唯一読んでない.

というか昨日知った.

もっと早く知りたかった.

実装

とあるコンサルタントのつぶやき - Part 1. 双方向データバインドの基本的な使い方

間違えなく実装面で一番参考になった. Part2も素晴らしい.

とあるコンサルタントのつぶやき - .NETとJavaの例外処理の違い

趣旨とは違うけど, Javaの勉強になった.

コントロール

Microsoft Developer Network - Windows フォーム コントロールの機能別一覧

あまりに無知なので読んだ.

Bug Catharsis - 常識的に考えてErrorProviderを使いたいよね

ごめんなさい.ごめんなさい.

be free - フォーカスがあるテキストボックスの背景色を変えるコンポーネント

ありがとうございました.

DataGridView

.net snippets - C# - SortableBindingList

これをVB移植して利用.

こいつがないとDataGridViewにバインディングしてもソートできない.

+++ 上野メモ帳 +++ 0 DataGridViewのDataSourceに独自クラスのListを指定する場合

DataGridViewに具体的にバインディングする.

方法上記と合わせて.

方法 : Windows フォーム DataGridView Cells でコントロールをホストする

これさえ読めばカスタムセルが作れる.

(コピペできる)

MS Report

インストール

Microsoft Developer Network - VS2015でレポート(ReportViewer)を使用したい

変更から入れるってマジでわからないっす.

Microsoft Rdlc Report Designer for Visual Studio

これ入れる必要あったのかよくわかってない.

SSDT for Visual Studio 2017

これも入れる必要あったのかよくわかってない.

Stack overflow - Reportviewer tool missing in visual studio 2017 RC

最終的にここの2番目の回答のモジュールに参照を全て通したら動いた.

あーリビルドはしたかも.

使い方

Microsoft レポートによる帳票の作成

素晴らしい教材.

導入以外は基本これで済んだ.

あとはサブレポートぐらいだけど, サブレポートはフィーリングでいける.

ReportViewerにおけるサブレポートの使い方について

そもそもサブレポートというものをこれで知った.

SQLite3

職人気質を目指すプログラマの日記 - System.Data.SQLite の GetDateTime でエラー

恐ろしくハマったので.

インストーラ

ドラブロ - Visual Studio 2017 でインストーラを作成する

すごい親切.

Web備忘録 - Visual Studio 2017 Installer Projects でインストーラーを作成する

こちらも中々親切.

疑似個人情報データ生成サービス

デモデータ作るのに使わせてもらった.

プロ生 - マイクロソフトが提供しているアイコンセットまとめ

大抵のアイコンはこれでいい気がする.

参考書籍

設計関連

  • .NETのエンタープライズアプリケーションアーキテクチャ第2版 .NETを例にしたアプリケーション設計原則
  • 実践ドメイン駆動設計
  • エンタープライズアプリケーションアーキテクチャパターン
  • エリック・エヴァンスのドメイン駆動設計

業務知識系

  • グラス片手にデータベース設計 販売管理システム編 第2版
  • グラス片手にデータベース設計 ~会計システム編
  • グラス片手にデータベース設計~生産管理システム編

dbext.vimの使い方

vimからDBアクセスできるという素晴らしいプラグイン.

導入

面倒なのでVundle使う. この辺は個人のお好みで.

以下をvimrcに記述.

Plugin 'vim-scripts/dbext.vim'

設定

プロファイル(接続情報)を書く必要がある.

MySQLの場合

当然だけど, MySQLクライアントが導入されている必要がある.

以下の設定の場合.

項目
プロファイル名 mysqlnodb
ホストIP 127.0.0.1
ユーザ名 root
パスワード password
DB名 test

.vimrcにこんな感じで書く.

let g:dbext_default_profile_mysqlnodb = 'type=MYSQL:host=127.0.0.1:user=root:passwd=password:dbname=test'

Oracleの場合

項目
プロファイル名 myORA
ホスト 127.0.0.1
ユーザ scott
パスワード tiger
let g:dbext_default_profile_myORA = 'type=ORA:srvname=127.0.0.1:user=scott:passwd=tiger'

PostgreSQLの場合

ちょっと試した限りではPostgreSQLが一番癖があった.

項目
プロファイル名 myORA
ホスト 127.0.0.1
ユーザ test_user
DB名 test_db
パスワード test_password
ポート 5432
let g:dbext_default_profile_postgres = 'type=PGSQL:host=127.0.0.1:user=test_user:dbname=test_db:passwd=test_password:port=5432'

記述自体はこれだけなんだけど, 実行すると下記の様に怒られる.

dbext:PostgreSQL requires a '$HOME/.pgpass' file in order to authenticate. This file is missing. If you are using a local connection, you can create an empty .pgpass file.  The b
inary 'psql' does not accept commandline passwords.

.pgpassを作ると怒られずに済んだ.

$ touch ~/.pgpass

あとロケール設定していないと怒られた.

SQLiteの場合

以下の設定の場合.

項目
プロファイル名 sampleSQLite
DBファイルパス ~/test.db
let g:dbext_default_profile_sampleSQLite = 'type=SQLITE:dbname=~/test.db'

プロファイルの外部ファイル化

プロファイルは.vimrcとは別管理したい.

そこで.vimrcに外部ファイルがあれば読み込む様に記述して, ~/.dbext_profileという名前のファイルに上記のプロファイル設定はまとめて書く事にした.

" プロファイルを記述したファイルがあれば読み込む
if filereadable(expand('~/.dbext_profile'))
    source ~/.dbext_profile
endif

履歴ファイル名を変える

履歴機能は便利なんだけど, ホームが汚れるのが嫌なのでファイル名を変えた.

let  g:dbext_default_history_file = '~/.dbext_history'

使い方

これだけあれば仕事にも使えそう.

Normalモード

コマンド 機能
slt テーブルの一覧を取得
sdt カーソル下のテーブル名のテーブル定義を取得
sdta 指定テーブル名のテーブル定義を取得
se カーソル下のSQLを実行
sbp 接続プロファイルの切替
sh Historyを表示
st カーソル上のテーブルをSelect

Insertモード

補完が便利っぽい.

*.sqlファイルを開いていない時には以下の様にfile typeを指定してやる必要がある.

:set ft=sql
コマンド 機能
<C-c>t テーブル名を補完
<C-c>v ビュー名を補完
<C-c>c カラム名を補完
<C-c>a シンタックスを補完
<C-c>L テーブル補完中に押すとカラムを全て出力

何気に辞書登録されているので

<C-x> + <C-k>

が使える.便利.