2012年12月4日

ASP.NETのWCFサービスでDbContextから取得したオブジェクトを返そうとするとエラーになる場合の対策

先日、ASP.NETのWCFサービスでEntityFrameworkのDbContext経由で取得したモデルクラスのオブジェクトを返そうとして、壁にぶつかった。

発生したエラーの内容はこんな感じ。
Test method ServiceTest.CustLoadData threw exception:
System.ServiceModel.CommunicationException:
An error occurred while receiving the HTTP response to http://localhost:60741/MyService.svc.
This could be due to the service endpoint binding not using the HTTP protocol.
This could also be due to an HTTP request context being aborted
by the server (possibly due to the service shutting down).
See server logs for more details.
---> System.Net.WebException: The underlying connection was closed:
An unexpected error occurred on a receive.
---> System.IO.IOException:
Unable to read data from the transport connection:
An existing connection was forcibly closed by the remote host.
上記のエラーはWCFサービスの呼び出し側で表示されるエラーなので実際に起こっているエラーの原因はちょっと違う部分にある。


いろいろデバッグに苦労したけれど、とりあえずどうやらWCFサービス内でオブジェクトを返すためにシリアライズしようとしている所でエラーが起きているらしい、という事が分かった。

原因は、DbContextの動作原理としてPOCOに対する変更追跡と遅延ローディングを実現するために内部的に「動的プロキシクラス」というものを生成している事にあるらしい。

動的に生成されたプロキシクラスはシリアライズ対象に出来る「既知のクラス」として認識されないのでエラーになってしまう様だ。

POCO エンティティの使用 (Entity Framework)
『Windows Communication Foundation (WCF) では、プロキシを直接、シリアル化または逆シリアル化できません。その理由は、DataContractSerializer では既知の型のみのシリアル化または逆シリアル化ができますが、プロキシ型は既知の型ではないためです。』

結局対策としては

this.Configuration.ProxyCreationEnabled = false;

の1行をDbContextクラスのコンストラクタ内に入れる事で解決した。
これでデータ取得時に動的プロキシが作成されずに純粋なPOCOが返って来るので、シリアライズ時に問題が発生しなくなる。


ちなみにMSDN内の下のページには「シリアル化中にProxyDataContractResolver クラスを使用してプロキシ型を POCO 型にマップ」する方法が載っている。でもちょっと試してみたところ上手く行かなかったので今回はパスした。
チュートリアル: WCF による POCO プロキシのシリアル化 (Entity Framework)


他の解決策として、動的プロキシの作成を抑止せずにWCFサービスでオブジェクトを返す前に手動でPOCOにデータを詰め替えるという方法もある。

データを詰め替えるのはAutomapperなどのライブラリを使えば簡単に出来るので、ProxyCreationEnabled を falseにするという解決策を取りたくない場合はそちらでも良いかも知れない。



その他参考になりそうなURL:

Entity Framework 4, WCF & Lazy Loading Tip | .NET Zone


Entity Framework v4 POCO templates: repository returns object of incorrect type - Stack Overflow





 

2012年11月23日

DotInstallのTodoアプリケーションをASP.NET MVC4/C#で作りなおしてみた


元のレッスンはこちら。
PHP/jQueryで作るToDoアプリ (全20回) - ドットインストール

作ったものはこちら。

Todo_ASPNETMVC4 - GitHub
Zipダウンロード 

元のアプリケーションがシンプルなだけに特に引っかかる箇所もなくサクサク進んだので楽しかった。

jQuery UIの sortable を使った事がなかったので良い練習になった!

あっ、そう言えば最初の準備段階でこんな問題があったりしてちょっと引っかかった。けどASP.NET MVC4のCode Firstの練習にもなった。^^



 

gitで既にコミットしてしまったファイルを無視する様に変更したい場合は

今日、ASP.NET MVC4の Code Firstを試していてgitでつまづいたので将来の自分用にメモ。

Code FirstでDbContextクラスを作ってモデルクラスを作って、Package Managerコンソールから「Update-Database」を実行すると、無事App_Dataフォルダにデータベースファイルが作成された。

モデルクラスに対応したテーブルも自動的に作成されていて、素晴らしい!

と感動したものの、考えてみるとこのデータベースファイルは出来ればgitの管理下には置きたくない。

ところが気付いた時点では既にGitHubにデータベースファイルもろともpushした後だった。

あわてて .gitignoreファイルに
*.mdf
*.ldf
の2行を追加した。

それから git rm -f *.mdfしてみたり試行錯誤してみたけれども、どうも上手く行かない。

まだまだ git の事を理解していない証拠だな~、とちょっと落ち込む。

とりあえず検索して何とか答えが見つかった。
pushした後に.gitignoreに追加したのが良くなかったみたいだ。

git - Visual Studio gitignore and mdf, ldf files - Stack Overflow


  1.  一旦.gitignoreから*.mdf, *.ldfの設定をコメントアウトする。
  2. git rm --cached -- *.mdf を実行。
  3. git rm --cached -- *.ldf を実行。
  4. git commit -m "コミットメッセージ" を実行。
  5. 1でコメントアウトした.gitignoreの設定を戻す。

この操作で無事データベースファイルが除外された。