Archive

Posts Tagged ‘VB.NET’

VB.NETで複数項目のグルーピングを行う時はKeyキーワードを

久々なのですっかり忘れていましたが、VB.NETの匿名型はC#とちょっと違うんですよね。

LINQのGroupByなんかで複数キーを設定する場合は、匿名型を利用するわけですが匿名型の比較の部分でVB.NETは少し癖が有ります。

    複数キーのグルーピング

例えば、こんなコードがあるとして、SubKey1とSubKey2でグルーピングされることを期待しますが、

Sub Main()
    Dim source1 = {
        New With {.Id = 1, .SubKey1 = "a", .SubKey2 = "1"},
        New With {.Id = 2, .SubKey1 = "a", .SubKey2 = "1"},
        New With {.Id = 3, .SubKey1 = "a", .SubKey2 = "2"},
        New With {.Id = 4, .SubKey1 = "a", .SubKey2 = "3"},
        New With {.Id = 5, .SubKey1 = "b", .SubKey2 = "1"},
        New With {.Id = 6, .SubKey1 = "b", .SubKey2 = "1"},
        New With {.Id = 7, .SubKey1 = "b", .SubKey2 = "1"}
    }
    Dim group1 = source1.GroupBy(Function(s) New With {s.SubKey1, s.SubKey2})
    For Each g In group1
        Console.WriteLine("{0},{1}={2}件", g.Key.SubKey1, g.Key.SubKey2, g.Count())
    Next
End Sub

結果はこうなります。グルーピングされていませんね。

a,1=1件

a,1=1件

a,2=1件

a,3=1件

b,1=1件

b,1=1件

b,1=1件

比較項目にKeyキーワードをつけると

匿名型で比較をする場合はKeyキーワードを付けてあげましょう。

Dim group1 = source1.GroupBy(Function(s) New With {Key s.SubKey1, Key s.SubKey2})
For Each g In group1
    Console.WriteLine("{0},{1}={2}件", g.Key.SubKey1, g.Key.SubKey2, g.Count())
 Next

グルーピングされました。

a,1=2件

a,2=1件

a,3=1件

b,1=3件

カテゴリー:プログラミング, Visual Basic タグ:

Oracle Data Provider for .NET 12.1のODP.NET管理対象ドライバ

2013年11月13日 1件のコメント

手違いで記事が消えてしまったので、11/13日の記事を再投稿します。

Oracleのトレース関係を調べていて今更ながら気づいたんですが、ODP.NETの最新版が7月にリリースされていたんですね。

Oracle® Data Provider for .NET開発者ガイド 12cリリース1 (12.1)
Oracle Data Provider for .NETの変更点
http://docs.oracle.com/cd/E49329_01/win.121/b72971/release_changes.htm#BABGBIEB

この中で気になったのが、ODP.NET管理対象ドライバというもの。ドキュメントを読む限りでは、ODP.NET 12.0.1以降ではODP.NETの本体であるOracle.DataAccess.Client.dllを置き換えるアセンブリ(と名前空間)として、Oracle.ManagedDataAccess.dllが利用できるようになり、マネージドなDLLだけでOracleに接続できるようになったらしい。しかもTNSなんかの設定もapp.configやweb.configで行えるらしい。

設定ファイルの修正

DbProviderFactories経由で利用している場合、設定ファイルの変更だけで対応ができます。例えばこんなコードで利用しているなら、web.configやapp.configに定義したconnectionStringのproviderNameをOracle.DataAccess.ClientからOracle.ManagedDataAccess.Clientに変更するだけです。

Dim connectionStringName = ConfigurationManager.AppSettings("DefaultConnectionStringName")
Dim connectionString = ConfigurationManager.ConnectionStrings(connectionStringName)
Dim factory = DbProviderFactories.GetFactory(connectionString.ProviderName)

Using connection = factory.CreateConnection()
connection.ConnectionString = connectionString.ConnectionString
connection.Open()
Using selectCommand = connection.CreateCommand()
selectCommand.CommandText = "select sysdate from dual"
Console.WriteLine(selectCommand.ExecuteReader().ToString())
End Using
connection.Close()
End Using

web.config/app.config

<connectionStrings>
<add name="OracleConString"
providerName="Oracle.ManagedDataAccess.Client"
connectionString="DATA SOURCE=XXX;USER ID=XXX;Password=XXX" />
connectionStrings>
OracleClientがインストールされていない環境で動かしてみる。

ODP.NET管理対象ドライバの基本的な機能は分散トランザクション関連の機能を除き、64bitと32bitで差が無いようです。ODP.NET_Managed121010.zipをダウンロードしてZIPファイルをインストールフォルダー(C:\odp.netあたり)に解凍します。インストールに関する詳しい記述はOracle Data Provider for .NET管理対象ドライバのインストールを参考にしてください。CPUアーキテクチャー毎に用意されたconfigure.batを実行します。64bit OSにインストールする場合は"C:\odp.net\managed\x64\configure.bat"ですね。

無題

動きました。

ODACがインストールされていない環境で動かしてみる。

あれ?マネージドだけで動くってことは、設定ファイルと必要なDLLをローカルコピーでおいてやれば、ODACなくても動くんじゃね?ってことで、app.configにDbProviderFactoriesのエントリーを登録して見ます。

<DbProviderFactories>
<clear/>
<add name="ODP.NET, Managed Driver"
invariant="Oracle.ManagedDataAccess.Client"
description="Oracle Data Provider for .NET, Managed Driver"
type="Oracle.ManagedDataAccess.Client.OracleClientFactory,
Oracle.ManagedDataAccess, Version=4.121.1.0,
Culture=neutral, PublicKeyToken=89b483f429c47342"/>
</DbProviderFactories>

DLLが無いとおこられたので、CPUに応じたOracle.ManagedDataAccess.dllを同じフォルダーに入れてあげます。

無題2

動きました。

image

関連ドキュメント

OracleClientをインストールしなくても動くのはいいですね。ただ、Oracle.DataAccess.Clientとは差異がアルようなのでODP.NET管理対象ドライバを利用する場合は、このあたりのドキュメントに目を通しておいたほうがいいでしょう。

With

2011年12月12日 1件のコメント

Visual Basic Advent Calendar 2011の12日目です。Atndはこちら。http://atnd.org/events/23108

どうせVBについて書くんなら、VBならではのことを書こう。とはいえ、気になるものはすでにブログの記事なんかにしているから、改めて何を書こうかネタを探すと難しいんだよね。C#には無いものだと、やっぱりMyとかWithとかのキーワードかなぁ。Myは微妙に消えてしまいそうな気もするのでWithかなぁ。

ということで、Withの新しい使い方、初期化子について触れてみます。

今の仕事上、まだまだVB6のプログラムを見ることが多いんだけれど、オブジェクト名のタイプを減らしたいのか、やけに長いスコープでWithを使っているプログラムがあるんですよね。一体このWithはどこにかかっているのか問い詰めたい感じです。

そういったWithの使い方は死んでしまえと思うわけですが、こと初期化に限っていえば、変数の初期化はここからここまででやっていますよーってのがわかるので、そのWithがどのオブジェクトかわかるならWithは短いスコープでどんどん使っていけ!!という感じはしています。もちろんどんなに短くてもネストは嫌です。

そんな意味では、VB9からの初期化子を使ったWithの使い方はさすがだなーと思わせます。

以前Twitterでつぶやいたことがあるんだけれど、拡張メソッドを集めているサイト(http://www.extensionmethod.net/)に、VBだからこそ力を発揮する拡張メソッド、Selfってのが紹介されています。定義はこんな感じです。

<Runtime.CompilerServices.Extension()> _
Function Self(Of T)(ByVal x As T) As T
    Return x
End Function

この拡張メソッドは、obj.Selfとするとobjを返却してくれるメソッドです。これだけだと微妙に何をしたいのかわからないのですが、VBのWithや初期化子を使うと、こんな感じでコードが書けます。

Module Module1
    Sub Main()
        Dim target = New Hoge With {
            .ThisObject = .Self,
            .String1 = "abc",
            .String2 = "123"
        }
        Console.WriteLine("{0} = {1}", target.String1, target.ThisObject.String1)
    End Sub
End Module

この初期化しを展開してしまえばそんな難しいことはやっていないし、どこで使うのと聞かれてもパットはでてこないけれど、ちょっと面白いでしょ。

カテゴリー:プログラミング, Visual Basic タグ:

ASPXファイルやjavascriptファイル、スタイルシートファイルもリンク参照したい。

2010年12月31日 1件のコメント

Visual Studioを使ったアプリケーション開発では、他のプロジェクトのクラスやリソースを利用したい場合は、アセンブリの参照を行ってクラスやリソースを共有します。しかしアセンブリの参照では、WPFとSilverlightのようにアセンブリの互換性がなかったり、テキストファイルなどを直接参照することはできません。

リンク参照とコンテンツファイル

複数のプロジェクトでファイルそのものを共有したい場合に利用するのが、リンク参照です。例えばWPFとSilverlightではアセンブリレベルの互換性は一部のアセンブリだけですが、ソースをリンク参照することで同じソースコードを利用してコンパイルすることができます。

複数のプロジェクトでファイルを共有するには?

この機能を使うと、ソースコードであれば問題なくビルドすることはできのですが、ファイルそのものを実行時に利用するようなHTMLや画像ファイルの場合は困ります。あくまでリンクがVisual Studioのビルド時に解決されるだけなので、実際のコンテンツファイルは参照先にしか存在しないので、ブラウザーなどでそのURLを見てもファイルが存在しないのです。

image image

プロジェクト上はCommonPageプロジェクトのASPXがリンク参照されているが、ブラウザーでそのASPXを参照すると、ファイルが存在しないエラーになってしまう。

リンクの解決とファイルのコピー

じゃぁビルド時にプロジェクトファイルの内容を見て、リンク先のファイルを自分のフォルダーにコピーしてあげればいいじゃん!!って事で、まずはプロジェクトファイルを確認してみましょう。プロジェクトファイルをテキストエディターなどでひらくと、リンク参照先は/Project/ItemGroup/Content/Linkに定義されいることが確認できます。

  <ItemGroup>
    <Content Include="..\CommonPages\pages\共通ページ.aspx">
      <Link>pages\共通ページ.aspx</Link>
    </Content>
    <Content Include="Default.aspx" />
    <Content Include="pages\個別ページ.aspx" />
    <Content Include="Web.config" />
    <Content Include="Web.Debug.config">
      <DependentUpon>Web.config</DependentUpon>
    </Content>
    <Content Include="Web.Release.config">
      <DependentUpon>Web.config</DependentUpon>
    </Content>
  </ItemGroup>

ここまでわかればLINQ to XMLでざっくりと取れるわけですが、どうせなら、VB.NETでXML Schemaをつくって、インテリセンス経由でリンク参照の一覧を取り出してみましょう。まずは、アイテムの新規追加から、Xml To Schemaを選択します。

新しい項目の追加 - ConsoleApplication1

次に表示さる、XMLドキュメントからのXMLスキーマセットの生成では、ファイルから追加ボタンをクリックして、リンク参照を含んだVBやC#のプロジェクトファイルを追加します。

XML ドキュメントからの XML スキーマ セットの生成

あとは、こんなふうにコードを記述すると、リンクとして追加したファイルの参照先の一覧を取得することができます。

Imports <xmlns:ns="http://schemas.microsoft.com/developer/msbuild/2003">

    ''' <summary>
    ''' リンクされているプロジェクトアイテムを取得します。
    ''' </summary>
    Private Shared Function GetLinkItems(ByVal projectFilePath As String) As Dictionary(Of String, String)
        Dim xml = XElement.Load(projectFilePath)
        Dim query = From element In xml...<ns:ItemGroup>.<ns:Content> _
                   Where element.<ns:Link> IsNot Nothing AndAlso _
                         Not String.IsNullOrEmpty(element.<ns:Link>.Value) _
                   Order By element.@Include

        Dim linkItems As New Dictionary(Of String, String)
        For Each item In query
            Console.WriteLine(String.Format("追加 {0}", item.@Include))
            linkItems.Add(item.@Include, item.<ns:Link>.Value)
        Next
        Return linkItems
    End Function

あとは、取得したファイルの一覧をループして、プロジェクトディレクトリーの相対パスにファイルをコピーしていくだけです。

        Dim linkItems = GetLinkItems(targetProjectPath)
        Dim projectDirectoryPath = Path.GetDirectoryName(targetProjectPath)
        Console.WriteLine("ファイルのコピーを開始します。")
        For Each linkItem In linkItems
            Dim copyFrom = Path.Combine(projectDirectoryPath, linkItem.Key)
            Dim copyTo = Path.Combine(projectDirectoryPath, linkItem.Value)

            Copy(copyFrom, copyTo)
        Next

ここで気をつけたいこととして、ソース管理ツールを使っている場合、ディレクトリーの中がからの場合はディレクトリーそのものが作られないことがあります。Visual Studioのリンクとして参照を利用すると物理ファイルが存在しないので、コピー先のフォルダーが存在しないことがあるので、コピーするときは必ずフォルダーの存在有無を確認して、フォルダーが存在しなかったら作成してあげる必要があります。

    ''' <summary>
    ''' ファイルのコピー(上書き)
    ''' </summary>
    Private Shared Sub Copy(ByVal CopyFrom As String, ByVal CopyTo As String)
        Dim dir = Path.GetDirectoryName(CopyTo)
        If Not Directory.Exists(dir) Then
            Directory.CreateDirectory(dir)
        End If

        Console.WriteLine(String.Format("[Copy]:{0}→{1}", CopyFrom, CopyTo))
        File.Copy(CopyFrom, CopyTo, True)
    End Sub

あとはこれを正常時は0を、異常時には1を返すコンソールアプリケーションにしてにしてあげれば、Visual Studioのビルド後動作に組み込むことができます。

ビルド後動作の追加

ビルド後の動作を設定したい場合、VBのプロジェクトであれば、プロジェクトのプロパティー⇒[コンパイル]⇒[ビルドイベント]から、C#であれば、プロジェクトのプロパティー⇒[ビルドイベント]から設定できます。

今回はWebプロジェクトのプロジェクトがビルドされた後に共通Webプロジェクトからファイルをコピーしたいので、ビルド後に実行するコマンドラインに次のようなコマンドを記述します。

 

“$(ProjectDir)..\Tools\ConsoleApplication1.exe” “$(ProjectPath)”

 

$()で記述されているのはVisual StudioというかMsBuildのマクロの一種です。ここではプロジェクトフォルダーの一つ上のToolsフォルダに作ったコピー用のコンソールアプリケーションを呼び出し、引数にプロジェクトファイルのパスを与えています。

ビルドを実行すると、出力ウインドウには次のようにログが出力されます。実際にエクスプローラーなどで確認してみると、リンクとして参照したファイルがコピーされているのを確認できます。

—— ビルド開始: プロジェクト: Page2, 構成: Debug Any CPU ——

Page2 -> C:\Source\LinkSample\Page2\bin\Page2.dll

追加 ..\CommonPages\pages\共通ページ.aspx

ファイルのコピーを開始します。

[Copy]:c:\Source\linksample\page2\..\CommonPages\pages\共通ページ.aspx→c:\Source\linksample\page2\pages\共通ページ.aspx

ファイルのコピーが終了しました。

image

動作確認

実際にWebブラウザーで確認すると、ちゃんと表示されるのを確認できます。

image

注意)ソリューションエクスプローラーなどでブラウザーで確認を行った場合、~/参照先の「Webサイト/参照したファイルのパス」が表示されてしまいうまく参照できませんが、Webブラウザーでちゃんとしたパスを指定すれば確認できます。

注意)あっそうだ、リンク参照とは別にCommonPageプロジェクトに対する参照も追加するのを忘れないようにしておいてくださいね。今回はCSファイルやVBファイルはコピーしていないので、コードファイルは参照先のものを使っていますから。CSファイルやVBファイルをコピーしたいときはコンソールアプリケーション側に手を入れてちょっと細工をしてあげてください。ダウンロードようのプロジェクトにはコメントとしてコードが入っているので参考にしてください。

今回のプロジェクトはここからダウンロードできます。

http://cid-b17f5fcd8095628b.office.live.com/self.aspx/%E5%85%AC%E9%96%8B/LinkSample.zip

すぐに値を返したい。VBも出来る子です。

昨日のサンプルでこんなコードブロックがありました。

 var path = context.Server.MapPath(
      ((Func<UserContext, string, string>)((u,appName)=>
            {
                if (appName == "app1" && u.UserId == "karua")
                    return @"\ClientBin\SilverlightApplication1.xap";
                else
                    return @"\ClientBin\SilverlightApplication2.xap";

            }))(userContext, app)

((Func<UserContext, string, string>)(u, appName) => {}))(userContext, app)ってなんか意味分かんないっすよね。無名のままだとダメだから名前をつけるか型を明示しないとコンパイラーに怒られるんですけれど。

実際にプログラミングするときは、コードブロックにする以上は意味が有るんだからメソッド分けろよという意見も有るんだろうけれど、ぼーっとプログラミングするときには、わざわざ別にするのも考えが妨げられて嫌だなーと。

で、これが実はVBだとこうかける。

Dim path = context.Server.MapPath(
    (Function(u, appName)
         If (appName = "app1" AndAlso u.UserId = "karua") Then
             Return "\ClientBin\SilverlightApplication1.xap"
         Else
             Return "\ClientBin\SilverlightApplication2.xap"
         End If
     End Function)(userContext, app))

キャストがいらない!!

以上でした。

カテゴリー:プログラミング タグ: