以前にLS-GLでmonoを試した時にADO.NETからMySQLへ繋いでみたのですが、気がつけばMySQLのADO.NET用ドライバ(Connector/Net)も6.3.6になっていました。ダウンしてみると中に4.0やらEntityのフォルダやファイルが存在する。もしや?と思いインストールしてみると.NET 4.0用のEntity対応のドライバでした。VS2010対応なのでDatabaseからEDMもワンタッチで作成できそうです。ということで、簡単な実験を行いました。
2003ServerにインストールしたVisualStudio2010で作業しています。前述のMySQLのドライバ(msiファイル)をフルにインストールしています。MySQLは別のLinuxマシンで動作しているものを使っています。
既存のMySQLに新たにテスト用のデータベースを作成し、そこにテーブルも作成しました。
ここで後のEntity Data Model(EDM)の作成時にハマったことですが、古いバージョンのMySQLで作成されたデータベースを、その後のアップデートされた新しいバージョンで使用しているとEDMの自動生成に「mysqlのprocが云々…」といったエラーが出て失敗してしまいました。Mac miniのMacPortsで使っているMySQLは5.0から5.1へと更新されていたので引っかかったのでしょう。mysql_upgradeやらやってみましたが直らず新しいバージョンでデータベースを構築し直してリストアすればいいのでしょうが、サーバとして使っているので下手に壊すと大変ということで、別のLinuxマシンに新規でMySQLをインストールして使うことにしました。
方針としては、何の変哲もないWindowsFormプロジェクトを作成し、Form上に配置したボタンのクリックでいろいろなテストメソッドを走らせるシンプル極まりないスタイルwで実験します。まずはプロジェクトを作成し、そこに[追加]-[新しい項目]でEntity Data Modelを作成します:
データベースから作成させます:
MySQLへの接続文字列などを指定しEDMを作成します。
こんな感じでウィザードを進めていくとEDMが作成されます。edmxファイルをVSで開くと例のテーブルの相関図が表示されます。リレーションとかちゃんと認識してますね。
同時にObjectContextから派生したコンテキストクラス(今回の場合はtestEntities)と各テーブルのEntityクラス(DTOみたいなもの?)が自動生成されました。Entity FrameworkはMSDNによるとこんなイメージなんですね:
Entity FrameworkではEDMに対してクエリを発行し間接的にデータベースにアクセスするようですが、それにもいろいろな手段が用意されているようです。取りあえずやっつけではありますが、コードを書いて動かしてみました:
Private Function SelDataEntitySQL(ByVal USERID As Integer) As M_USER Using entity As New testEntities() Dim query As String = "SELECT VALUE a FROM testEntities.M_USER AS a WHERE a.USERID = @p1" Dim result As IEnumerable(Of M_USER) = entity.CreateQuery(Of M_USER)(query, New Objects.ObjectParameter("p1", USERID)) Dim rs As M_USER() = result.ToArray() If rs.Length > 0 Then Return rs(0) Else Return Nothing End If End Using End Function
Private Function SelDataStoreQuery(ByVal USERID As Integer) As M_USER Using entity As New testEntities() Dim result As IEnumerable(Of M_USER) = entity.ExecuteStoreQuery(Of M_USER)("select M_USER.* from M_USER where M_USER.USERID = ?p1", New MySql.Data.MySqlClient.MySqlParameter("?p1", USERID)) Dim rs As M_USER() = result.ToArray() If rs.Length > 0 Then Return rs(0) Else Return Nothing End If End UsingEnd Function
Private Function SelDataNativeSQL(ByVal USERID As Integer) As M_USER Using entity As New testEntities() Try entity.Connection.Open() ' MySQLのネイティブConnectionを取得 Dim cn = DirectCast(entity.Connection, EntityClient.EntityConnection).StoreConnection ' MySQLのネイティブCommandを生成 Dim cmd = cn.CreateCommand() cmd.CommandText = "SELECT * FROM M_USER WHERE USERID = ?p1" cmd.CommandType = CommandType.Text ' MySQLのネイティブParameterを生成 Dim p1 = cmd.CreateParameter() p1.ParameterName = "p1" : p1.DbType = Data.DbType.Int32 : p1.Value = USERID cmd.Parameters.Add(p1) ' MySQLのネイティブReaderを実行 Dim result As M_USER = Nothing Using rdr = cmd.ExecuteReader() While (rdr.Read()) Dim rec = New M_USER() With {.USERID = rdr("USERID"), .KANJ_NAME = rdr("KANJ_NAME"), .KANA_NAME = rdr("KANA_NAME"), .BIRTH = rdr("BIRTH")} result = rec End While End Using Return result Catch ex As Exception Throw ex Finally Try entity.Connection.Close() Catch End Try End Try End Using End Function
更にLINQを使ったアクセスがありますが割愛。最後のMySQL.Data.ClientのConnectionを使った直接アクセスは、使っていいものか否かわかりませんが、各DBに特化した機能を使いたい場面はそこかしこであると思われるので、逆に使えないと困ります。
データソースを抽象化して意識させずにデータを操作するといった概念は理解出来ますが、その為にかえって不便を強いられるなら意味をなさない気もします。SQLに似て非なるEntity SQL、言語の仕様を拡張したLINQ文のラムダ式。RDB以外のデータソースに対しては有効かもしれませんが、果たしてどうなんでしょうね?