IsolationLevel.ReadCommittedが安全なケース

データベースにはロックのポリシーとして、分離レベルというのがある。
このレベルを高めれば、データの一貫性を維持できるが、その分ロックされる時間が増え、結果としてパフォーマンスに影響したり、一貫性を損なう処理をした場合の対応について考慮しなくてはいけなくなる。
 
最も分離レベルが高いのをSerializableという。これを設定すると、読み取り中にデータの変更ができない。書き込み中は読み取れないという、あらゆる矛盾を排除するようなロックがかけられる。そのかわりロックがかかっている間、処理がブロックされるのでパフォーマンスが落ちる。
 
ほとんどのデータベースはデフォルトでReadCommittedという分離レベルを使う。これはSerializableよりもロックが甘く、トランザクション中でもコミット済みデータの読み取りを許可する。ただし、途中で削除処理のトランザクションが完了すると、読み取り中のデータと矛盾してしまう。だから反復読み取りには矛盾が発生する可能性がある。
 
 
でも、アプリケーションの用途によっては、というかたいていの場合、ReadCommittedは安全なのだ。
 
例えば、月末で確定したデータを月初に帳票処理する業務アプリの場合、読み取るデータは全て確定済みなのだからそのデータに関しては変更されることがない。だから現実的には矛盾は発生しないし、そういう処理はアプリケーションで禁止してしまうことができる。
 
とはいえ、そう簡単に割り切れないケースもあるだろう。アプリケーション側でロックを実装する必要があるかもしれない。
 
 
並行処理が必要ないなら何にしたってかまわない。
 
自動的にデータを収集するシステムなどで、いつデータが入ってくるのか分からない場合はロックの少ないReadComittedを使うことでブロックしないアプリケーションが作れる。
 
OracleやSQLServerのような高度なデータベースはReadCommitted Snapshotなどというようなものを使えるらしい。
 
 

C#のプロパティに初期化の方法があればいいのにと思った

C#のプロパティは宣言と一緒に初期値指定することはできない。フィールドと組み合わせるか、コンストラクタで初期化するとかしないといけない。

class Person
{
    string _Name = "名無し"; // フィールドでやるか
    public string Name
    {
        get
        {
            return _Name;
        }
        set
        {
            _Name = value;
        }
    }
    public Person()
    {
        Name = "名無し"; // コンストラクタでやるか
    }
}

これまでは値をセットしたときにイベントを起こすとか、設定ファイルと連動させるときとかに使ったことがあったくらい。

もしかしたら次期バージョンではできるのかなと思って、「C# プロパティ 初期 .net framework 4.0」とかで調べてみるけど、無さそうですね。

そのかわりちょっと興味をひく記事を見つけました。

[C#]自動プロパティの必要性
[C#]自動プロパティの必要性(その2)

出水さんという方のコメント「アセンブリ公開するのがわかっていれば、もともとフィールドでは置かないですしね」

なるほど、先日からCOMを使っているので意味分かります。インターフェースにフィールドは置けないんですよね。メンバ変数みたいなのをCOMに公開したければパブリックプロパティを使う必要があります。

インターフェースに初期化データを置くというのも変な気がするので、その辺で初期化は導入されないのかもしれないですね。詳しいことは知りませんが。

DataGridViewをActiveXコントロールにラップしてTcl/Tkのウィンドウに埋め込む(2)

 

前回に引き続き、イベントの定義をやってみました。

DataGridViewのボタンセルが押されたら、そのセルの行・列番号を引数にして、Tclのプロシージャをコールバックするということがしたい。

ソリューション一式: SimpleDgv.zip

まず、メソッドと別にインターフェースを用意して、InterfaceTypeをComInterfaceType.InterfaceIsIDispatchにします。

[Guid("1AE7D7D7-02EF-4d70-B7F5-71CE046FAEA9"), ComVisible(true), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ISimpleDgvDispatch
{
    void ButtonClick(int col, int row);
}

そして、クラスの属性にComSourceInterfaces(“ISimpleDgvDispatch”)を追加し、クラスにButtonClickイベントを定義します。

[Guid("29E1BC35-88D3-47f0-997D-B889CA25E135"), ComVisible(true), ClassInterface(ClassInterfaceType.None), ComSourceInterfaces(typeof(ISimpleDgvDispatch))]
public partial class SimpleDgv : UserControl, ISimpleDgvInteface
{
    public delegate void ButtonClickDelegate(int col, int row);
    public event ButtonClickDelegate ButtonClick;

    protected void OnButtonClick(int col, int row)
    {
        if (ButtonClick != null)
        {
            ButtonClick(col, row);
        }
    }
}

ビルドするとCOMとして公開されました。

Tcl側のコード

proc ButtonClick {col row} {
    tk_messageBox -type ok -message "Callback Col=$col, Row=$row"
}
optcl::bind $dgvObj ButtonClick ButtonClick

 

さあ、どうでしょう?

エラー全文

イベントを実行したC#側で例外が発生しました。例外テキストからはSystem.RuntimeType.InvokeDispMethodに渡されるcultureが不明なものであったということだろうと解釈できますが、だからといってどうすればよいというのでしょう?

DISP_E_UNKNOWNLCIDとはなんぞや?ググっても文字通りの説明しか出てきません。

AssemblyInfo.csでCultureInfoを指定したりもしてみたが、何も変わらない。

これで丸1日試行錯誤したものの、全く原因も解決法も分からず途方にくれました。

どうしよう。引っ込みがつかない。


2009/9/8 -- 追記: 成功

.Net Framework フォーラムで質問したらjzkeyさんが回答をくれました。

http://social.msdn.microsoft.com/Forums/ja-JP/netfxgeneralja/thread/46e0c5a2-0a89-472e-b4d6-0cd7399e1300

動くようになったoptclのバイナリとソースを置いておきます。

http://yyamasak.drivehq.com/devel/src/tcl/lib/optcl3010t-bin.zip
http://yyamasak.drivehq.com/devel/src/tcl/lib/optcl3010t-src.zip

optclがビルドできるようになったので、とりあえずBase64でのやり取りで妥協したマルチバイト引数の件も調べれば何とかなるかもしれないという希望が出てきました。

DataGridViewをActiveXコントロールにラップしてTcl/Tkのウィンドウに埋め込む(1)

先日Tcl/TkのGUIにデータグリッドを、ということでTkTableをいじっていましたが、やっぱりいろいろ大変だということで、.NET FrameworkのDataGridViewをTkのウインドウに埋め込む実験をしてみました。これならほとんどデフォルトのバインディングでも文句はないでしょう。
 
さて、C#で作ったユーザーコントロールをActiveXコントロールにする方法は比較的簡単です。プロジェクトのプロパティでそれっぽいところに2箇所ほどチェックを入れてビルドすればCOM参照可能になります。
 
ビルドしてできたDLLはP/Invoke的な方法で利用することはできないです。マネージド環境の外から使うため、COMに登録する必要があります。
 
手動でインストールする場合は、WindowsSDKに含まれるregasm.exeを使って、
regasm SimpleDgv.dll /tlb:SimpleDgv.tlb /codebase
 
アンインストールは
regasm SimpleDgv.dll /unregister
 
COMへの登録に成功したら、次はいかにTclから利用するかです。ActiveTclには標準でtcomというCOMを利用するためのパッケージが付いてきますが、ActiveXコントロールをTkに埋め込むという目的には使えません。代わりにOptclというパッケージが存在します。これはActiveTclに含まれていないため、Tcler's Wikiにあったリンクからコンパイル済みのバイナリをとってきました。
 
System.Windows.Forms.dllがそのままCOMにできればいいなと一瞬思ったんですが、そうしたところでOptclで使うのが難しい引数や型もあるので、あまり意味がない気がします。必要そうなものに限ってラッパーメソッドを作って公開することにします。
 
以下の図は、上がC#のフォームにユーザーコントロールを貼り付けたもの(これはCOMではありません)。下の方が、optclを使ってActiveXコントロールとしてTkのウインドウに貼り付けたものです。
 
 
日本語が文字化けしていますね。どうしてこうなるのか、よく分かっていません。
文字列はソースファイルに埋め込んであります。
 
TclのソースはShiftJISで、C#はUTF-8 BOM有りとなっています。
聞くところによると、Tclは内部エンコーディングとしてUTF-8を、C#はunicodeを採用しているそうですが、その辺の問題でしょうか?
optclが出す(C#のエラーメッセージと思われる)も文字化けしてて復元できないんですよね。
 
いろいろといじってみてはいますが、解決していません。
 
encoding convertto unicode
encoding convertto cp932
 
とかは意味なかったです。このコマンドもあまり理解してないんですけどね。
 
C#でバイト列から文字コードを判定する方法はあちこちで紹介されていたので、Tclから渡すときに文字列をbinary formatしてbyte[]で渡そうと思ったらoptclの制限で配列引数は未実装とのこと。
 
うーん困った。
 

2009/9/2 -- 追記: とりあえずC#の側で解決しました。

byte[] b = Encoding.Default.GetBytes(s);
string u = Encoding.UTF8.GetString(b);

要するに、TclはUTF-8をデコードして送ってるのに、C#はシステムのデフォルトエンコーディング(Shift_JIS)としてエンコードしてるようなのです。

だからその逆をたどってやればよいわけで、まず文字化けした文字列をShift_JISとしてバイト列に戻します。これをUTF-8エンコードしてやることで、本来の文字列に戻してやることができるという理屈です。ただし、これはもともとUTF-8で送ってくるクライアントにしか対応できません。2行目が決めうちだからです。


2009/9/2 -- 追記: やっぱだめ

うまくできたと思ったのはひらがなだけで、漢字やカタカナは一部が文字化けしてしまいました(例: 選択 -> 選・)。

ここによれば、一度間違って変換されたものは可逆性を失うようです。
http://dobon.net/vb/dotnet/string/getencoding.html

最初から不可逆なんだからC#で直すことはできないってことになる。

あとはTclからそもそもShift_JISで送れるようにするか、base64でASCIIとして送って、デコードするかしかなさそう。


2009/9/2 -- 追記: 成功

やっぱりbase64でやることにした。いちいち変換しないといけなくてめんどいけど、しょうがあるまい。

Tcl側のポイントは、マルチバイト文字を含む場合は一旦バイナリに変換しないといけないこと。encoding converttoを使う。
C#側はShift_JISのASCII部分だけ使うので、情報の損失がなくなった。返すときは任意のエンコーディングでかまわないようだが、Tcl側で対称性を持たせるためにシステムデフォルト(Shift_JIS)で返すようにした。

Tcl側のコード(エンコーディング省略でシステムデフォルト)

proc dec {s} {
 ::base64::encode [encoding convertto $s]; # Tcl -> C#
}

proc enc {s} {
 encoding convertfrom [base64::decode $s]; # C# -> Tcl
}

C#側のコード

namespace Extension
{
    public static class StringMethod
    {
        public static string Dec(this string s, Encoding encoding) // Tcl -> C#
        {
            var b = Convert.FromBase64String(s);
            b = Encoding.Convert(Encoding.Default, encoding, b);
            return encoding.GetString(b);
        }

        public static string Enc(this string s) // C# -> Tcl
        {
            var b = Encoding.Default.GetBytes(s);
            return Convert.ToBase64String(b);
        }
    }
}


Visual Studio 2008 C#ソリューションとTclのソースを固めて置いときます。ご自由に拾ってください。
 
 
 

今後の予定:
  1. イベントの定義(ボタンクリックとか)
  2. クリップボード操作(CSV/TSVプレーンテキスト)
  3. その他もろもろ

TkTableのバインディングを修正した

TkTableはdllで配布されていますが、ヘルプに説明のあるデフォルトのバインディングはlib/Tktable2.9/tkTable.tclに書かれているようです。
このファイルは特にデモアプリなどで使われているわけではなく、単にカスタマイズする人のために内部の実装を見えるようにしたものだと思われます。

TkTableはオプションで-selectmode extendedを指定することでドラッグによる範囲選択ができるようになるのですが、セル領域とタイトルセル領域にまたがってドラッグした場合、選択領域がばらばらになってしまうという問題がありました。

最新の2.10に添付されたtkTable.tclも全く変わってなかったので、修正してみました。ただ、過去の選択部分だけをクリアするような処理を、全域クリアに変えてしまったので、パフォーマンス的には問題ありの可能性もあります。ためしに3000行×50列とかでやってみたところでは全く問題ありませんでした。
 

--- C:/Tcl/lib/Tktable2.9/tkTable.tcl	Tue Aug 25 20:01:59 2009
+++ C:/devel/tcl/tktable/tkTable.tcl	Fri Aug 28 18:59:51 2009
@@ -433,26 +433,54 @@
 	    if {[catch {$w index anchor}]} { return }
 	    scan $Priv(tablePrev) %d,%d r c
 	    scan $el %d,%d elr elc
-	    if {[$w tag includes title $el]} {
-		if {$r < [$w cget -titlerows]+[$w cget -roworigin]} {
-		    ## We're in a column header
-		    if {$c < [$w cget -titlecols]+[$w cget -colorigin]} {
-			## We're in the topleft title area
-			$w selection clear anchor end
-		    } else {
-			$w selection clear anchor [$w index end row],$c
-		    }
-		    $w selection set anchor [$w index end row],$elc
-		} else {
-		    ## We're in a row header
-		    $w selection clear anchor $r,[$w index end col]
-		    $w selection set anchor $elr,[$w index end col]
+
+	    set titlerowend [expr {[$w cget -titlerows]+[$w cget -roworigin]}]
+		set titlecolend [expr {[$w cget -titlecols]+[$w cget -colorigin]}]
+	    if {$r != $elr} {
+			if {$r < $titlerowend} {
+				if {$elr >= $titlerowend && $r >= $titlerowend} {
+					set elr $r; # turn aside while selecting title row area
+				}
+			} else {
+				if {$elr < $titlerowend && $r < $titlerowend && $elc >= $titlecolend} {
+					set elr $r; # turn aside while selecting non-title row area
+				}
+			}
 		}
+		if {$c != $elc} {
+		    if {$c < $titlecolend} {
+				if {$elc >= $titlecolend && $c >= $titlecolend} {
+					set elc $c; # turn aside while selecting title col area
+				}
+			} else {
+				if {$elc < $titlecolend && $c < $titlecolend && $elr >= $titlerowend} {
+					set elc $c; # turn aside while selecting non-title col area
+				}
+			}
+		}
+		$w selection clear all
+	    if {[$w tag includes title $r,$c]} {
+			if {$r < $titlerowend} {
+			    if {$c < $titlecolend} {
+					## We're in the topleft title area
+					if {$elc >= $titlecolend} {
+					    $w selection set anchor [$w index end row],$elc
+					} else {
+					    $w selection set anchor $elr,[$w index end col]
+					}
+			    } else {
+				    ## We're in a column header
+				    $w selection set anchor [$w index end row],$elc
+			    }
+			} else {
+			    ## We're in a row header
+			    $w selection set anchor $elr,[$w index end col]
+			}
+		    set Priv(tablePrev) $elr,$elc
 	    } else {
-		$w selection clear anchor $Priv(tablePrev)
-		$w selection set anchor $el
+			$w selection set anchor $el
+		    set Priv(tablePrev) $r,$c
 	    }
-	    set Priv(tablePrev) $el
 	}
     }
 }

Tcl/TkからC#へ

大学でC++に挫折して、プログラマはあきらめていた私はサークルのホームページでPerl/CGIを使ったのをきっかけに再びプログラミングに手を染めることになりました。

それでも仕事で使えるレベルではないだろうと、会社に入った時点ではプログラマになることは断っていたのですが、会社のCRM立ち上げに携わった後に部署がなくなり、開発部にまわされました。

最初にやったのがカメラのPTZ制御プログラムにPELCO-Dプロトコルを追加するというC++の小さな案件でした。それはそれっきりで、次にやったのがTcl/Tkのタッチパネル式のGUIアプリケーションをベースにした開発案件でした。それ以来、3年ほどWindowsアプリケーションをTcl/Tkで作ることになりました。

Tclの自由度は高く、XOTclなどでオブジェクト指向言語のようにも使えます。C#のクラスはプロパティやメソッド以外にイベントというメンバを持つことができるのですが、これだってTclからすればさして特別な機能ではありません(コンテキストのキャプチャは確かに難しいですが)。Ruby on RailsのActiveRecordのようにスキーマを自動的にマッピングする機能はType-safeな方法では実現できないので、C#には同様のライブラリは存在しません(全てを文字列として扱えばよいだけなので、不可能というわけではないです。誰もやらないというだけで)が、Tcl/Tkでコンセプトを真似た簡易版を作って使っています。

しかし、Tcl/Tkの言語本体はそこそこのスピードで進化していくものの、ライブラリ群は放置されてるものが多いです。また、2002年くらいを境に日本語の書籍の発行は途絶えています。オブジェクト指向拡張はあるものの、Rubyみたいに自由度の高いXOTclにはActiveTclのIDEやデバッガが対応していません。

Tclは世間のうわさに反して優れた言語だと思うし、そこそこライブラリも充実してはいるのですが、やはり完璧なアプリを作るに当たって、いろいろと問題があるのは否めません。

BLT Toolkitはすばらしい拡張ライブラリですが、Tcl/Tkの最新版に対応していません。stubに対応すべくGeorgeさんががんばって更新しているようですが、依然完成のめどが立たない状況が続いてます。

ほかにも結構重要なライブラリがちょっとしたバグを残したまま放置された状態です(TclUDP, Tktableとか) 。

teapot化されたライブラリをEXEにラップするにはTclAppをバージョンアップしないといけないし。

新規開発に関しては既にTcl/Tkをあきらめて、C#を採用しています。Tclの自由度に比べれば堅苦しさは否めませんが、.NET Frameworkなら高品質なGUIが開発できるので、安心してお客さんに見せられます。

Tcl/Tkには印刷ダイアログを表示する機能がありません。DataGridView並みに高度なGUIがありません。TkTableやTablelistがあるじゃないか、というかもしれませんが、これらはDataGridViewに比べて非常にプリミティブです。

大学の研究室や社内で使うならまだしも、簡単にGUIを作れるというのは、現代においてもはや売り文句にならない気がします。

C#などのGUIを生かしつつTclの既存アプリを再利用することはできないのでしょうか?これについてはいろいろと調べてきましたが、なかなか有効な手段は見つかりません。WindowsであればCOMの仕組みを使うことで何とかいけるみたいな話(optcl)はありますが、果たして実際にやった人はいるのでしょうか?

エクスポート関数はともかく、引数の型が構造体だったりして、構造体のメンバがさらに構造体だったりしたら、どうするの?とか。

C++の方が相性はいいだろう。MFC DataGridというのが使えたらいいけど、クラスのインポートってできるの?とか。

TclBridgeというのは?高いなあ。ちゃんとメンテされてるの?

Eagleは?Tclの再実装?うーんそういうことじゃないんだよなあ。

じゃあ何ができたらいいのでしょう?

ネイティブのTclに.NETのクラスライブラリをインポートして、publicなクラスや構造体を自由に操作したい。これだ!

私にCの知識と遊んでいても給料払ってくれる社長がいたら多少貢献できると思うのだけど。

・・・いや、無理。私では力不足です。

こうしてネットのところどころでこういうボヤキを書いているのは誰かが作ってくれるかもとちょっと期待してるからなんですが、まあ、そんな都合よくはいかないよな。。。日本語だから外人読めないし。

夢の対決 投手ロボVS.打者ロボ

これはたしかにすごいけど、投手ロボの立場が微妙だなあ。

仮に150キロの球を投げられるようになっても大リーグの投手にはかなわないけど、
そのボールを100%打てる打者の方がレアだから、打者ロボの方が先に人間を超えてしまう。

これでは、技術的には高度なはずの投手ロボの注目度が低くなってしまう。

だから、さらに発展させて三本指でキャッチするロボットを作って欲しい。
三本指で150km/hのキャッチボールとなると、まさにロボットって感じがする。
それに両方とも対等だからどちらかが注目度が低くなって悲しむこともないし。

http://headlines.yahoo.co.jp/hl?a=20090724-00000097-san-soci

Default Isolation Level

Entity Framework+SQLiteで、IsolationLevelを設定する方法が分かりました。

接続のプロパティ、または設定ファイルを直接編集して、
connectionStringのdata sourceに、default isolation level=ReadCommittedを追加。

やっぱり単純なことだった。

しかし、実際の挙動を見た感じでは、テーブル単位でロックがかかるみたいです。
同じテーブルで読み込みを実行している間は、database is lockedエラーが発生しました。

別のテーブルであれば、保存に成功します。

まあ、それなら今までやってきた次善策も全部が無駄になったわけではない。とはいえ、複雑な気分。
結局のところ、読み込み中の保存は別途キャッシュしないといけないということか。

IsolationLevelとしては、他も一応選べるようだったので、もしかしたらSerializable/ReadComitted以外も使えるものがあるかもしれません。

dotConnect for SQLiteのお試し版を試用中。設定のオプションがSQLite.NETより多く、ReadUncomitted=falseがデフォルトなことから、ReadComittedがデフォルトということだろうか。しかし、SaveChangesでエラーが出て使えない。値段が高いわりにはいろいろと面倒な感じ。

Entity Frameworkを使ったプロジェクトを一通り終えて

しょっちゅう更新しようと考えていたブログでしたが、途中からそんな余裕もなく、
3ヶ月休みなしで出勤しつつ、極めつけは客先(宿泊施設)に3日間滞在しつつようやく終えることができました。

さて、ほとんどの問題は解決することができたものの、いまだ2つだけ心残りがあります。

ひとつは、MVC的な方法を適用したとき、バリデーションがモデル内で完結できない点です。
NOT NULLな属性の入力値がNULLであることの検証は、コントローラにしかできません。
まあ、全部Nullableにすればいいのかもしれませんが。

もうひとつは、トランザクションがフレームワークまかせになっている点です。

LINQ to Entities + SQLiteでの話なので、他のサーバクライアント型のDBを使った場合は問題ないのかもしれません。

SQLiteの場合、IsolationLevelはSerializableかReadCommittedが使えるらしいですが、デフォルトはSerializableになっています。

マルチスレッドアプリケーションで、バックグラウンドで時間のかかる帳票作成などしていると、場合によっては並行して書き込み処理が発生することがあります。
というか、今回のアプリではいろんなところからパケットを受信してはDBに保存するので、読み込みと書き込みがバッティングするのは普通に起こっていました。

その場合に読み込み処理の方が、Serializableでトランザクションを実行していると、SaveChanges()の方がタイムアウト待ちになって、しかも読み込み処理の方も影響を受けてどちらも無意味に遅くなります。

かといって、ReadCommittedを使ってみると、なぜかSaveChanges()メソッドに失敗して変更が保存されません。

結局、並行処理はあきらめて、読み込み処理中には保存処理は全て拒否して、
別途ファイルに保存しておいたイベントデータを暇なときに読み込むという方法をとるしかありませんでした。

それから、これは解決することができたんですが、メソッドチェーンでWhereの条件を追加しながら結果を絞り込む方法を使うときは、クエリの遅延実行が働くようになっているようなんですが、これを下手に使うとループや属性へのアクセスごとに接続が実行されて、非常に遅くなります。

条件が少ない最初の絞込みのときに、ToArray()かToList()などを使って、メモリに展開しておくのがよいようです。
クエリの複雑さにもよりますが、私のLet’s Note Y2 PentiumM1.5GHz+768MBのPCでは30倍以上の差が出ました。

これで、帳票作成のクエリにかかる時間が最大1分程度になったため、書き込み処理をブロックしてもそれほど業務に影響はないかな、と思ってとりあえず妥協しました。ただ、たまたま今回のプロジェクトではそれでよかったですが、読み込み中はぜんぜん書き込めないというのでは問題になることが多いのではないかなあ。

もっと直接的にコマンドを発行する方法もあるようなので、今度はそれで検討してみようか。

時期バージョンのVisual StudioではIronRubyが使えるらしいから、そのときはRailsのActiveRecordが使えるといいなあ。

LINQ to Entities + SQLiteでCAST(expression AS type)

前回のエントリにて、アルファベットと数字の混在する部屋番号というのを扱ったけれど、結局、文字部分を削除しても問題ないということになったので、それはまあ、良くなった。実はいろいろ調べてAlphanumeric sortというのがあると知ったのだが、まあ、いろいろ役にたつことだろう。

さて、それならわざわざ文字列にしなくてもよいだろうということで、INTEGERに変更した。それでずいぶん無駄なロジックやキャストなんかが減って、すっきりした。

しかし、そこに新たな要求が来た。

  1. 部屋番号の範囲を指定して、複数の居住者を検索する。
  2. 部屋番号を文字列とみなして前方一致検索する。

1. の場合、INTEGERなので問題ない。

2. の場合、WHERE句で文字列にキャストしないとLIKEが使えない。クライアントサイドでの絞込みは馬鹿らしい。

正直こんなもん、SQLiteなら文字列も数値も同じだから、LIKEが使えていいんじゃないのかと思うけど、LINQ to Entitiesではそうもいかない。ObjectQuery<T>のWhereは条件に文字列とプレースホルダが使えるから、その線で探してみた。CASTを使えばよいらしい。Googleで調べるのに2時間もかかったよ。はー疲れた。

ObjectQuery<User> userQuery = entities.User.Include("Room")
  .Where("CAST(it.Room.room_no AS System.String) LIKE @keyRoom",
  new ObjectParameter("keyRoom", keyRoom + "%"));

CAST (expr AS type) というのがSQLiteにもあって、それは結構すぐ見つかったけど、typeのところがC#の型になるとは思い浮かばず、TEXTか?VARCHAR(4)とかか?と意味不明なエラーに苦しんでいました。System.Data.SQLiteのせいじゃないかと思って、dotConnect for SQLiteを自腹で買おうか真剣に悩んで見つけたサンプルをCASTでgrepしたら見つかりました。

Download dotConnect for SQLite