ActiveDirecotryでログインユーザーがどのグループに属しているか調べる

実は、ActiveDirectry を扱うために DirectoryEntry, DirectorySearch なんかを駆使しないと駄目なのかなぁ、と思ったのですが、単純に現在ログインしているユーザーに関してならば、ローカルの Windows ユーザーをチェックすれば OK でした、というオチです。

# ActiveDirecotry 特有のプロパティを調べる場合は、DirectoryEntry などが必要になるのですが、 カレントユーザーのグループ名の列記だけならば、結構簡単に、という話です。

ユーザ名やドメイン名は、Environment のスタティックプロパティを利用します。
グループ名の一覧を取得する時は、NTAccount クラスにキャストして使います。

という訳で簡単にソースをば。

private void Form1_Load(object sender, EventArgs e)
{
	string username = Environment.UserName;
	string domainname = Environment.UserDomainName;

	labelUser.Text = username;
	labelDomain.Text = domainname;

	// using System.Security.Principal;
	WindowsIdentity user =
		WindowsIdentity.GetCurrent();

	// 属しているグループ名一覧を取得
	listBox1.Items.Clear();
	foreach (var group in user.Groups)
	{
		// NTアカウントに変換
		NTAccount ac =
			(NTAccount)group.Translate(typeof(NTAccount));
		listBox1.Items.Add(ac.Value);
	}
	listBox1.Sorted = true;
}
カテゴリー: 開発, C# | ActiveDirecotryでログインユーザーがどのグループに属しているか調べる はコメントを受け付けていません

DataGridView から Excel へコピーすると文字化けする

Visual Studio 2010 と Excel 2010 の組み合わせだと発生しないのですが、
Visual Studio 2005 と Excel XP の間だと文字化けが発生します。

3流プログラマのメモ書き : (VB.Net)DataGridViewのショートカットキーでのコピー(Ctrl+C)をExcelに貼り付けると文字化けする
http://jehupc.exblog.jp/9119842/

な感じで、Ctrl+C をフックしないと駄目か…と思っていたのですが、良い方法があったので up してきます。

1.DataGridView を継承して、ExDataGridView を作る。
2.以下のように GetClipboardContent をオーバーライドして Text フォーマットのみ返すようにする。

Public Overrides Function GetClipboardContent() As System.Windows.Forms.DataObject
    Dim org As DataObject = MyBase.GetClipboardContent()
    Dim dat As New DataObject
    dat.SetText(org.GetText(TextDataFormat.Text))
    Return dat
End Function

どうやら、HTML形式で clipboard へコピーしているので、Excel のほうがこの HTML 形式を貼り付けようとして文字化けしています。VB のほうは(多分)UTF-8 でコピーしているのに、Excel のほうで SJIS しか対応していない、というのが主原因ですね。

Excel 2010 を使っている場合は、文字化けしないので ok なんですが。

こんな感じで、クリップボードにコピーすると、

こんな感じで、文字化けしてしまいます。

なので、GetClipboardContent をオーバーライドして変更

カテゴリー: 開発, VB | 2件のコメント

Assembly.LoadFrom を使って別のアセンブリにあるフォームを起動する

試してみると、意外と素直に書けたのでメモ。

とあるプロジェクトで、フォームが100画面ほどある VB プロジェクトを作りました。
その VB プロジェクトを起動するときに、なんらかの形で、main window を切り替えたいという要望です。

# フォームごとにアセンブリを分ければ良いのですが、
# フォーム間で参照設定が多くて、結局ひとつのアセンブリにしないと循環参照が解決されない…という現象です。
# 本当は、メモリ使用量を減らすために、個別に exe を作るのがいいんでしょうが。

フォームが 100 画面ほどある exe にパラメータを指定して、

FormCollection Form1 

で起動します。

Module Startup
    Public Sub main(ByVal args() As String)
        ' 引数からクラス名を取得
        Dim cname As String = args(0)
        cname = "FormCollection." + cname
        ' 指定フォームを作成
        Dim t As Type = Type.GetType(cname)
        If t IsNot Nothing Then
            Dim obj As Object = System.Activator.CreateInstance(t)
            If obj IsNot Nothing Then
                ' アプリケーションを起動
                Dim frm As Form = CType(obj, Form)
                Application.Run(frm)
            End If
        End If
    End Sub
End Module

ここでは、namespace が FormCollection なので、これを追加しています。

さて、本体 exe の名前を変えて Form1.exe, Form2.exe, Form3.exe … と作ってもよいのですが、本体 exe のサイズが大きい場合には、コピーすると HDD 容量が必要になります。

なので、起動用に別のプロジェクトを作って、次のような startup にします。

Module Startup
    Public Sub main(ByVal args() As String)
        ' クラス名を実行ファイルから取得
        Dim cname As String = Application.ExecutablePath
        cname = cname.Substring( _
            cname.LastIndexOf("\") + 1, _
            cname.Length - cname.LastIndexOf("\") - 1)
        cname = cname.Substring(0, cname.LastIndexOf("."))
        cname = "FormCollection." + cname
        ' アセンブリのロード
        Dim asm As System.Reflection.Assembly = _
            System.Reflection.Assembly.LoadFrom("FormCollection.exe")
        ' 指定フォームを作成
        Dim t As Type = asm.GetType(cname)
        If t IsNot Nothing Then
            Dim obj As Object = System.Activator.CreateInstance(t)
            If obj IsNot Nothing Then
                ' アプリケーションを起動
                Dim frm As Form = CType(obj, Form)
                Application.Run(frm)
            End If
        End If
    End Sub
End Module

ってな風なのを考えた訳ですが、本体のロード時間を考えると必要なフォームだけをかき集めた exe を作るのがベストですね。
まぁ、本体が 3 MB 程度なので、これでよしとしますか。

カテゴリー: 開発, VB | Assembly.LoadFrom を使って別のアセンブリにあるフォームを起動する はコメントを受け付けていません

Visual Basic の if 文は歴史を覆すことができる…訳はない

技術メルマガネタ

青木「歴史に if は無いというが、Visual Basic には if があるッ!!!」
田中「まぁ、そりゃそうでしょうね。if 文がないと条件分岐ができないし…」
青木「しかし、if が無ければ、前だけ向いて進めばいいという先生の話も理解できるし」
田中「いや、それ、ラジオの CM ですし…」
青木「しかし、if が無ければ、管直人の支持率もアップアップだったわけだし」
田中「if というか、それ現実ですし…というか、アップアップって…」
青木「しかし、if が無ければ、小学校の思い出が、今現実のものとなり、今ここで発現されたチェレンコフ光が」
田中「if というか、訳わからないし…しかも、最後が微妙に危ないし…」
青木「まぁ、あれだよな。if とか select とか無いほうが、物事一直線で安全だよな」
田中「いや、猪突猛進管内閣自暴自棄暴走茫漠ということで」

これも却下ネタ。
技術メルマガでは、政治ネタはオミット。

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim i1 As Integer = 0
        Dim i2 As Integer = 2

        ' 単純な = 演算子
        If i1 = 0 Then

        End If
        ' 否定のための <> 演算子
        If i1 <> 0 Then

        End If
        ' 比較演算子
        If i1 < 0 Then

        End If
        ' Else も使うよ
        If i1 = 0 Then
            ' マッチする場合
        Else
            ' マッチしない場合
        End If
        ' ElseIf も使うよ
        If i1 = 0 Then
            ' 最初の条件にマッチする場合
        ElseIf i1 = 1 Then
            ' 2番目の条件にマッチする場合
        Else
            ' それ以外
        End If

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

        ' 2 つの条件を指定する
        Dim s1 As String = &quot;tonny&quot;
        Dim s2 As String = &quot;masuda&quot;

        ' 両方の条件を一度に満たす
        If s1 = &quot;tonny&quot; And s2 = &quot;masuda&quot; Then

        End If
        If s1 = &quot;tonny&quot; AndAlso s2 = &quot;masuda&quot; Then

        End If
        ' どちらか一方の条件を満たす
        If s1 = &quot;tonny&quot; Or s2 = &quot;masuda&quot; Then

        End If
        If s1 = &quot;tonny&quot; OrElse s2 = &quot;masuda&quot; Then

        End If

        ' ベン図を使う
        If s1 <> &quot;tonny&quot; Or s2 <> &quot;masuda&quot; Then

        End If
        ' 複雑なので not を使う
        If Not (s1 = &quot;tonny&quot; And s2 = &quot;masuda&quot;) Then

        End If
        ' 条件が多くて分かりづらい場合は、if 文を重ねると良い
        If s1 <> &quot;tonny&quot; Then
            If s2 <> &quot;masuda&quot; Then

            End If
        End If

    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click

        Dim s1 As String = &quot;tonny&quot;
        Select Case s1
            Case &quot;tonny&quot;
                ' tonny の場合
            Case &quot;masuda&quot;
                ' masuda の場合
            Case Else
                ' 上記以外
        End Select

        ' 実は if else と同じ
        If s1 = &quot;tonny&quot; Then
            ' tonny の場合
        ElseIf s1 = &quot;masuda&quot; Then
            ' masuda の場合
        Else
            ' 上記以外
        End If

        ' : を使って短く書ける
        Dim s2 As String = &quot;&quot;
        Select Case s1
            Case &quot;tonny&quot; : s2 = &quot;トニー&quot;
            Case &quot;masuda&quot; : s2 = &quot;増田&quot;
            Case Else : s2 = &quot;トニー 増田&quot;
        End Select

        ' 関数を使ってシンプルに
        Dim s3 As String = Check(s1)

        ' 文字列の場合は、Dictonary を使う方法もある
        ' あらかじめ設定しておく
        Dim dic As New Dictionary(Of String, String)
        dic.Add(&quot;tonny&quot;, &quot;トニー&quot;)
        dic.Add(&quot;masuda&quot;, &quot;増田&quot;)
        ' 辞書から検索
        If dic.ContainsKey(s1) = True Then
            s3 = dic(s1)
        Else
            s3 = &quot;トニー 増田&quot;
        End If

    End Sub

    ''' <summary>
    ''' SELECT 内で Return を使う
    ''' </summary>
    ''' <param name=&quot;str&quot;></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function Check(ByVal str As String)
        Select Case str
            Case &quot;tonny&quot; : Return &quot;トニー&quot;
            Case &quot;masuda&quot; : Return &quot;増田&quot;
            Case Else : Return &quot;トニー 増田&quot;
        End Select
    End Function

End Class
カテゴリー: VB, 技術メルマガ | 1件のコメント

Visual Basic の文字列をこよなく愛すれば、咳してもひとり

技術メルマガのネタ

青木「文○列と言えば、な~んだ?」
田中「先輩…伏字にしても、文字列としてか読めないんですけど」
青木「まあぁ、そうかもな。伏字にすると、アレだから、文♡列」っていうのはどうだ?」
田中「先輩…伏字というか、なんというか、文字列ですよね。やっぱり」
青木「そうかぁ、そうなると、文字列♡ っていうのは、どうだ?」
田中「…」
青木「ほら、あだち充が開発した、なんでもラブコメになってしまうという、技を君は知らないのか?」
田中「…」
青木「コホン…ああ、秋空。空が高いなぁ」
田中「盆には早いですが…」

だいたい、これだけ覚えておけば大丈夫よ、ってな感じです。
個人的には String.IsNllOrEmpty は使いたくないんですけどね。確かに String 型は Nothing/Null が入るので、null を代入したときに Length プロパティでチェックすると例外が発生してしまう訳ですが、データベースでの NULL を扱うならば、明示的に Nullable を使うほうが他との対応(nullable(of integer)とか)がよいかなぁと。

Imports System.Data.SqlClient

Public Class Form1

    '''
    ''' 基本的なところをちょろっと
    '''
    ''' <param name=&quot;sender&quot; />
    ''' <param name=&quot;e&quot; />
    '''
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim s1 As String = &quot;普通の初期化&quot;
        ' NULL を入れることができる
        Dim s2 As String = Nothing
        ' Empty を入れることができる
        Dim s3 As String = String.Empty
#If False Then
        If s2.Length = 0 Then
            ' 例外発生
        End If
#End If
        If s3 = &quot;&quot; Then
            ' 実は空白と同じ
            MessageBox.Show(&quot;実は空白と同じ&quot;)
        End If

        If String.IsNullOrEmpty(s2) = True Then
            MessageBox.Show(&quot;IsNullOrEmpty を使う&quot;)
        End If

        ' 直接文字列に対してメソッドを使える
        Dim s4 As String = &quot;tonny masuda&quot;.ToUpper
        MessageBox.Show(s4)

    End Sub

    '''
    ''' String は値型扱いになる
    '''
    ''' <param name=&quot;sender&quot; />
    ''' <param name=&quot;e&quot; />
    '''
    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        Dim src As String = &quot;tonny&quot;
        Dim dest As String = &quot;&quot;
        ' 文字列はコピーされる
        dest = src
        ' コピー元を変えても、コピー先は変わらない
        src = &quot;TONNY&quot;
        MessageBox.Show( _
            String.Format(&quot;src is {0}. dest is {1}&quot;, src, dest))
    End Sub

    '''
    ''' ToString を使ってフォーマットする
    '''
    ''' <param name=&quot;sender&quot; />
    ''' <param name=&quot;e&quot; />
    '''
    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        ' 日付をフォーマット
        MessageBox.Show(Date.Now.ToString(&quot;yyyy年MM月dd日&quot;))
        ' 数値をフォーマット
        Dim i As Integer = 1000
        MessageBox.Show(i.ToString(&quot;#,###円&quot;))
        ' クラス名など
        Dim cn As New SqlConnection
        MessageBox.Show(cn.ToString)
        ' ToString を overrides する
        Dim hello As New Hello
        hello.age = 40
        hello.name = &quot;tonny masuda&quot;
        MessageBox.Show(hello.ToString)
    End Sub

    '''
    ''' 文字列の連結諸々
    '''
    ''' <param name=&quot;sender&quot; />
    ''' <param name=&quot;e&quot; />
    '''
    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click

        ' + 演算子で連結
        Dim s1 As String = &quot;tonny&quot; + &quot; &quot; + &quot;masuda&quot;
        ' & 演算子で連結
        Dim s2 As String = &quot;tonny&quot; & &quot; &quot; & &quot;masuda&quot;
        ' 数値から暗黙の変換
        Dim s3 As String = 1234
        ' 数値へ暗黙の変換
        Dim i4 As Integer = &quot;1234&quot;
    End Sub

    '''
    ''' よく使われる String のメソッド
    '''
    ''' <param name=&quot;sender&quot; />
    ''' <param name=&quot;e&quot; />
    '''
    Private Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
        Dim s1 As String = &quot;tonny masuda&quot;

        ' 文字列の長さ
        Dim i2 As Integer = s1.Length
        ' 先頭の文字
        Dim s3 As String = s1.Substring(0, 1)
        Dim s4 As String = s1(0)
        ' 最後の文字
        Dim s5 As String = s1.Substring(s1.Length - 1, 1)
        Dim s6 As String = s1(s1.Length - 1)
        ' 大文字/小文字
        Dim s7 As String = s1.ToUpper
        Dim s8 As String = s1.ToLower
        ' 前後の空白を削除
        Dim s9 As String = s1.Trim
        ' 置き換え
        Dim s10 As String = s1.Replace(&quot;masuda&quot;, &quot;増田&quot;)
        ' 指定文字列を削除
        Dim s11 As String = s1.Replace(&quot;禁則事項ですッ!!!&quot;, &quot;&quot;)
        ' 先頭の5文字だけ残す
        Dim s12 As String = s1.Substring(0, 5)
        Dim s13 As String = s1.Remove(5)
        ' 左寄せ/右寄せ
        Dim s14 As String = s1.PadRight(20)
        Dim s15 As String = s1.PadLeft(20, &quot;○&quot;)
        MessageBox.Show(s15)

    End Sub
End Class

'''
''' ToString を overrides したクラス
'''
'''
Public Class Hello
    Public age As Integer
    Public name As String

    Public Overrides Function ToString() As String
        Return String.Format(&quot;オラ {0}. 年齢 {1} 歳だけど、よろしくなッ!!!&quot;, name, age)
    End Function
End Class

カテゴリー: VB, 技術メルマガ | Visual Basic の文字列をこよなく愛すれば、咳してもひとり はコメントを受け付けていません

Visual Basic の変数を解説するよ(技術メルマガ編)のメモ

青木「今日から、いよいよ変〇の解説をするぞッ!!!」
田中「ええ、あの、変…なんですか???」
青木「変〇の解説だッ!!!」
田中「変…というと、奥浩哉さんの漫画のアレですか?」
青木「変〇というとそうかもしれないな」
田中「あの、HEN…とかいう漫画のアレですか?」
青木「そうかもな、変〇というと、アレかもな」
田中「でも、あの、Visual Basic と何か関係あるんでしょうか?」
青木「そりゃ、変数が無いと困るだろう、Visual Basic 2010 でプログラミングできないじゃんッ!!!」
田中「…」
青木「何考えてんだ、君は???」

とかいう前振りを考えたのだけど、技術メルマガでは却下することにしました。アレだし。沼正三や天野哲夫の話をしても困るだろうし。でも、澁澤龍彦は良さそうだったり。

というのはさておき、Visual Basic では変数の扱いが意外とややこしくて、主な使い方が以下の通り。

' オブジェクト型
Dim obj As Object
' オブジェクト型(型指定をしない)
Dim obj2

' 数値型
Dim num As Integer
' 文字列型
Dim str As String

' 配列型
Dim array() As Integer
Dim array2 As Integer()

' クラスオブジェクト
Dim cn As SqlConnection
' コンストラクタを利用
Dim cnx As New SqlConnection

' コレクション
Dim lst As List(Of Integer)

' 推論
Dim var = From n In lst Select n

' 同時に初期設定する
Dim n1 As Integer = 10
' 同時に初期設定する(推論を利用)
Dim n2 = 10

Dim cn1 = New SqlConnection
Dim cn2 As Object
' オブジェクト型に代入
cn2 = cn1
Dim cn3 As SqlConnection
' オブジェクト型から代入
cn3 = cn2
' 明示的にキャスト(CType)を使って代入
cn3 = CType(cn2, SqlConnection)

' for で使う一時変数
For i As Integer = 1 To 10

Next

' for each で使う一時変数
For Each n In lst

Next

' リソースを解放させる
Using bmp As New Bitmap

End Using

If str = "" Then
	Dim temp As String = ""
End If
' ここでは使えない
' temp = ""

VB6.0 の頃に object 型だったり、Variant 型だったりしたものが、初期値で型推論に変わったので、このあたりが微妙に異なっていたり、string 型から object 型にキャストをするときに暗黙のキャストが定義されていたり。

関連としては、For 文と For Each 文と同時利用が必須ですね。ブロック内の局所変数とかも。

カテゴリー: VB, 技術メルマガ | Visual Basic の変数を解説するよ(技術メルマガ編)のメモ はコメントを受け付けていません

Visual Studio 2010 の Windows Azure Tools 1.4 は日本語版が無い

ちょっとメモ的に、

http://windows.azure.com/

にアクセスすると、Azure の管理ポータルが出て来ます。いつの間にか日本語化されているので便利ですね(ちょっと前までは英語版だけだった)。

で、Visual Studio 2010 で Azure のプログラミングをする場合は、Windows Azure Tools を入れればよい訳で、右側にある「Windows Azure Toolsのインストール」をクリックして、

Download Windows Azure SDK
http://www.microsoft.com/windowsazure/sdk/

なるところから「Get Tools & SDK」をクリックすると Web Platform Installer が実行されて IIS とかの設定もやってくれるので便利…とか思っていたわけですが、これでインストールされるバージョンが Azure Tools の「1.4」でして、このバージョンは日本語化がされていないんですよね。

  1. 3 のバージョンじゃないとだめなので、

ダウンロード詳細 Windows Azure SDK および Windows Azure Tools for Microsoft Visual Studio (2011 年 3 月)
http://www.microsoft.com/downloads/ja-jp/details.aspx?FamilyID=7a1089b6-4050-4307-86c4-9dadaa5ed018

なところからダウンロードしてインストールすることになります。

Azure Tools のバージョンは Visual Studio 2010 の「ヘルプ」→「Microsoft Visual Studioのバージョン」で開くと見れます。

書籍の発売までに、Web Platform Installer のバージョンでも日本語が通るといいんですけどね。

カテゴリー: 雑談 | Visual Studio 2010 の Windows Azure Tools 1.4 は日本語版が無い はコメントを受け付けていません

Visual Basic のリファクタリングポイント(上海編)

名著「リファクタリング」に沿って、リファクタリングせよッ!!! というのは難しいので、具体的なポイントを出します。

この前に前提条件があって、

1.NUnit で程よく、テストコードが書かれていること。
2.バージョン管理ツール(VSSやSVNなど)が導入されていること。

が条件になります。

「程よく」Nunit が使われているというのは、関数の中身を弄るので NUnit のテストコードを通しながらリファクタリングしたい、ということです。注意深くやれば特にテストコードはいらないのですが、手順を間違えたとき(人為的なミスですね)の発見が早くなります。

同時に、間違えたソースを元に戻すのにバージョン管理ツールが必要です。実はこれも必須というわけではなく、自前でバッグアップを取りながらでもよいのですが、修正前の差分や、一気にバージョンを戻してしまうことも可能なので、バージョン管理ツールがあると安全になります。

どちらも、【セーフティネット】の役割が強いです。

さて、リファクタリングと言えば、普通は関数の共通化が主な作業になるのですが、今回は【コード自体の質】を上げることに注力します。と言うのも、ぽろぽろと妙な書き方をされているところが多く、コードが統一されていないので、後からの保守が大変かつシステム試験時に顧客要望を取り入れにくい(コードを早急に改修しないといけない場合に、時間が掛かってしまう)という欠点があります。

・デバッグしやすいコードに直し、デバッグ時間を減らす。
・保守し易いコードに書き直し、保守可能なコードにする。

のが主目的です。

という訳で、リファクタリング対象の中で、公開しても差し支えないところをいくつか晒しておきましょう。コードは Visual Basic なのですが、C# にもあてはまります。

■if 文に not を使わない

関数が Function Method() As Boolean で作成されているときに、True/False で返すものだから、

if obj.Method() Then
  ...
end if

とやりたくなるのですが、

if not obj.Method() Then
  ...
end if

のコードが散見されたので、True/False 付きに変えることにしました。

if obj.Method() = True Then
  ...
end if

あるいは

if obj.Method() = False Then
  ...
end if

にして、明示的に boolean と比較します。こうすることで、デバッグ時にどちらで比較しているのか一瞬で分かるようになります。

まぁ、主原因は↓なコードがあって、何をやっているやら…って感じだったのが本音です。

if not obj.Method() <> 1 Then
  ...
end if

■if 文内で end sub/function した時は else を書かない

具体的には、

if param < 0 Then
  ' 異常系
  ...
  end sub
else
  ' 正常系
  ...
end if

よくある、if 文でパラメータなどをチェックしてエラーをはじくという処理なのですが、else の前で exit sub(return なども) をして関数を抜けています。なので、else のところって意味がないですよね。

普通に↓な風に書きましょうよ、という話です。

if param < 0 Then
  ' 異常系
  ...
  end sub
end if
' 正常系
...

実は、これ、えらいコードがあって、↓なコードがありました。

if param1 < 0 then
  ' 異常処理
  end sub
else
  if param2 < 0 then
    ' 異常処理
    end sub
  else
    if param3 < 0 then
      ' 異常処理
    else
      ' 正常処理
      ...
    end if
  end if
end if

正常系の処理をやりたいのやら、異常処理がやりたいのやら分かりません…ってな具合。

■関数の戻り値は意味のある場合だけチェックする

ちょっとローカルルール臭いのですが、こんなコードになります。

function Method() as Boolean
  ...
  if ... then
    ' エラーの場合、例外を発生させる
    throw new Exception(...)
  end if
  return true
end function

という関数があります。戻り値が True しかないのに、Function にしているのがおかしい、のは確かに言えて、本来は sub にしますね(C# ならば void型)。

これを利用するときに、律儀に、

if obj.Method() = false then
  ' 異常系
  ...
  end sub
end if

しているコードが結構あるのですが、まぁ、Function だし戻り値を確認しないといけないのは筋なんですが…。これをいざ、保守しようとしたときに Method が True/False を返すのか? って調べないと駄目なんですよね。

なので、内部で例外しか発生させていないし、その例外は catch しているわけではないので、単純な処理の記述として書き換えます。

obj.Method()

実は、今回のソースコードのローカルルールで「基本的に例外を発生させない」というものを作りました。DB を扱うので、SqlCommand 関係のエラー(タイムアウト等)しか発生しないので、この手の例外はもっと外部のほうで取っています。
という理由があって、例外処理をごちゃごちゃやりたくなかった、というのがあってのリファクタリング対象です。

まぁ、本来は Fuction じゃなくて、Sub にするのが正式ですかね。

■DataTable.Rows(0) を直接参照しない

今回は、DAO という形でデータベースアクセスをするクラスを必ず作りました。
O/R マッピング的に言えば、DAO 自体にメソッドを追加するのですが、設計時間的に余裕がなかったので、そのあたりは飛ばして、DataTable を直接扱うところが多々あります(実際には、自作の型付DataTableに自動マッピングさせていますが)。

この中で、主キーなどで検索した場合、必ず1件あるとう想定で書かれているコードが結構あったので、これをリファクタリング対象にしています。

dim dt as new datatable
da.fill( dt )

dim id as integer = dt.rows(0)("ID")
dim name as string = dt.rows(0)("NAME")
...

データベース的に整合性があっていれば、1件は取得できるためここで落ちることはないのですが、初期データの絡みもあって(現状のコードの品質も加味して)、Rows.Count をチェックします。

dim dt as new datatable
da.fill( dt )
if dt.Rows.Count = 0 then 
  exit sub
end if

dim row as datarow = dt.rows(0)
dim id as integer = row("ID")
dim name as string = row("NAME")
...

一時変数の row を使うのは、dt.Rows(0) があちこちに出てこないようにするためです。
元のコードのほうが、dt.Rows(0) を参照していることが明確になるのは確かなのですが、保守するときに「それは、Rows(1) ではないのか?」という迷いがなくなります。
なので、VB の場合は With 構文を使っても ok です。

dim dt as new datatable
da.fill( dt )
if dt.Rows.Count = 0 then 
  exit sub
end if

With dt.Rows(0)
	dim id as integer = .Item("ID")
	dim name as string = .Item("NAME")
end with

■トランザクションの範囲は短くする

これはバグ含みなので、リファクタリングというよりも修正ですね。

dim trans as new SqlTransaction
try
	dim cn as new SqlConnection("")
	...
	trans.Connection = cn.BeginTransaction()
	if パラメータチェック Then
	  ' 異常処理
	  return False
	end if
	...
	trans.Commit()
catch ex as SqlException
    trans.RollBack
    return False
end try
return true

というコードが散見されているのです。実際には、SqlTransaction, SqlConnection は別のクラスで実装していますが、これの書き方では駄目ですね。パラメータチェックで異常処理をした後に Rollback ができていない…というか、パラメータチェックの前でトランザクションを開始しては駄目でしょう。

if パラメータチェック Then
  ' 異常処理
  return False
end if

dim trans as new SqlTransaction
try
	dim cn as new SqlConnection("")
	...
	trans.Connection = cn.BeginTransaction()
	...
	trans.Commit()
catch ex as SqlException
    trans.RollBack
    return False
end try
return true

こんな風に、データベースアクセスとは関係ないものは try の前に書かないと駄目です。例外が発生されない訳だし。

これはコードを量産するときに起きる間違いで、

– まずは、関数を始めたら try – catch で囲む
– データベースを更新するときは、begintransaction を書いてしまう

という思い込みが問題になっています。本来は、

– データベースアクセス時には例外が発生するので、try-catch する。
– データを更新する範囲だけを、トランザクションで囲む

という書き方にしないと…。

そんなリファクタリングの日々だったり。

カテゴリー: 開発 | Visual Basic のリファクタリングポイント(上海編) はコメントを受け付けていません

宣言ッ!!! 技術メルマガを作るよ(´・ω・)ス

なんか、アレ、人のやる気を殺いではいけない、という意味で公開宣言しておきます。

.NET 技術系のメルマガ(有料)を製作中です。

全130回(週5回配信、26週間)をひと区切りにして、ラジオ講座のように学べるのが売りです。

・基礎VB講座
・基礎C#講座
・応用VB講座
・応用VB講座

と以下続く。

書きかけど、サンプルはこんな感じで↓

1.3 Visual Basic 2010 をインストール

■今日のトピック

Hello Everybody !!! こんにちは、Tonny マスダです。
第2回では定番の Hello World を作ったので、今度はメッセージボックスを使ってみますよ。小さいことからコツコツと、たまにはドーンと大きくやりますけど、今のところはコツコツと地道に進めてくださいねッ!!!

さて、Visual Basic 2010 を使って最初の定番プログラミング「Hello world.」を表示させたものの、その次の一手がわからない田中君。お次は何をするのでしょうか?

田中「先輩、ほら、Hello world ができましたよ(えへん)」
青木「おお、ああ、当たり前だな」
田中「え?」
青木「あのなぁ、Hello world が出たぐらいで喜んでちゃいけないのよ。次の一手ってのがあるだろう?」
田中「(あの、ちょっと、それって)」
青木「ほら、世の中 Hello world ばかりじゃ、Hello world プログラミングで溢れかえってしまう。そういうときは・・・」
田中「そういうときは?」
青木「昼にはこんにちは、夜にはこんばんわプログラミングだろうがッ!!!」
田中「ええッ???」

というわけで、いきなり、こんにちは or こんばんわプログラミングということになった田中君。さてはて、どうすればいいのでしょうか?

■今日のサンプルコード

今日は昨日使った Hello world のプログラムを修正して使います。
ボタンをクリックした時のコードは次の通りです。

If 9 <= Date.Now.Hour And Date.Now.Hour <= 17 Then
    MessageBox.Show(&quot;今日は&quot;)
Else
    MessageBox.Show(&quot;今晩は&quot;)
End If

さあ、これで準備は完了です。動作確認をしてみましょう。
そのまま動かす場合は「F5」キーを押せば ok です。

よく分からないと思いますが、そのままコードを打ち込むか、メールマガジンからコピー&ペーストしてくださいね。

■サンプルの解説

今回は条件分岐をするための if 文を使っています。
Visual Basic では Date.Now で現在の時刻が使えます。時計の「時」の部分をとるためには「.Hour」を付けます。これで、9 時から 17 時(午後5時)までの時間を示すことができます。

最初の「今日は」を表示するのが、9 時から 17 時。それ以外は「今晩は」を表示させています。

■構文の解説

If 条件 Then
  実行文
End If

If 条件 Then
  条件にマッチした場合
Else
  条件にマッチしなかった場合
End If

If 条件1 Then
  条件1にマッチした場合
ElseIf 条件2 Then
  条件2にマッチした場合
Else
   その他の場合
End If

■練習問題

問題1

 メッセージボックスの表示を、「お早う」「今日は」「今晩は」の 3 つに分けて変更してみましょう。「お早うは」5 時から 10 時。「今日は」は 11 時から 18 時。「今晩は」は 19 時から次の日の 4 時まで、にしてください。

■ワンポイントレッスン

価格は未定ですが、ラジオ講座よりも安いかなぁと。

独習として使うのもよし、新人研修に使うのもよし、ベテランの復習に使うのもよし、という感じで。

ちなみに、建設予定地はこちら

協力者は、こちら↓

飲む(´・ω・)ス|WEB系技術電脳日記
http://ameblo.jp/konica/entry-10970423610.html

カテゴリー: 仕事 | 宣言ッ!!! 技術メルマガを作るよ(´・ω・)ス はコメントを受け付けていません

Visual C++ 2010 では DataSet が作れない…ので C# から借用する

Visual C++ 2010 の C++/CLI を使うと、ADO.NET を扱うことができるのですが、なぜか Visual C++ 2010 では型付の DataSet が作れないという…へんな具合です。

試しに、Visual C++ 2005 を使ってみると DataSet を作れるので、故意に落としてしまったが、忘れてしまったかという感じ。
ちなみに、C# で作った DataSet のファイル(DataSet1.xsd)を、VC++ のプロジェクトにインポートするとうまく動きます。

ただし、インポートしたままだと、ビルドする時に DataSet1.h というファイルを自動的に作成したときに接続文字列が解決できません。
これは、もう一度 DataSet のデザイナを開いて、テーブルを作り直すか、*.xsd のファイルを開いて、接続文字列のところを直接書き換えます。C# では、アプリケーション設定から持ってきているのですが、VC++ の場合は直接、接続文字列を書くようにします。

<Connections>
 <Connection ConnectionStringObject=&quot;Data Source=.\SQLEXPRESS;Initial Catalog=sampledb;Integrated Security=True&quot; IsAppSettingsProperty=&quot;False&quot; Modifier=&quot;Assembly&quot; Name=&quot;sampledbConnectionString&quot; ParameterPrefix=&quot;@&quot; Provider=&quot;System.Data.SqlClient&quot;>
 </Connection>
</Connections>

こうすると、型付の DataAdapter と DataSet が作られるので、次のようにバインドします。

private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {

			 DataSet1TableAdapters::t_personTableAdapter ^da =
				 gcnew DataSet1TableAdapters::t_personTableAdapter();
			 DataSet1::t_personDataTable ^dt =
				 gcnew DataSet1::t_personDataTable();
			 da->Fill( dt );
			 this->dataGridView1->DataSource = dt ;
		 }

Windows XP 上ですが、一応実行例ということで。

カテゴリー: 開発, C++ | Visual C++ 2010 では DataSet が作れない…ので C# から借用する はコメントを受け付けていません