ExpresswebのSQLServerでメンバシップ機能を使用する
web.config設定について
ビルド別に使用DBを分けたい場合(例えば、デバッグ時はローカルの*.MDFを使用。本番環境はホスティング先SQLServerを使用する。)
Web.Release.configにホスティング先SQLServerへの接続文字列設定を書くことで切り分けができる。
<?xml version="1.0" encoding="utf-8"?> <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> <connectionStrings> <add name="<設定名>" connectionString="Server=<サーバー名>;Database=<DB名>;Uid=<ユーザー>;Password=<パスワード>;" providerName="System.Data.SqlClient" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" /> </connectionStrings> </configuration>
ExpresswebでSQLServerCe4を使用する
- VisualStudio2010 Pro SP1
- ASP.NET MVC 3 Webアプリケーション
- ターゲットフレームワークは.NET4
- ADO.NET Entity Data Model
で確認
はじめに
SQLServerは3つまでしかDBを作成することができない。これは嬉しくないのでSQLServerCompact4を使うことにした。
インメモリデータベースを使うのは初めてで、現状はホスティング先で使用可能であることを確認しただけなので、
これから先色々SQLServerCe4に起因する事象が発生すると思われる。
Web.config
コネクション文字列
*sdfを作成してテーブル作成、それを使用してモデル作成しただけなのでVSによる自動生成されたものを使用している。
<configuration> <connectionStrings> <add name="Database1Entities" connectionString="metadata=res://*/Models.Model1.csdl|res://*/Models.Model1.ssdl|res://*/Models.Model1.msl;provider=System.Data.SqlServerCe.4.0;provider connection string="Data Source=|DataDirectory|\Database1.sdf"" providerName="System.Data.EntityClient" /> </connectionStrings> ... </configuration>
アセンブリバージョン指定
こんな感じで指定する。
<configuration> ... <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <dependentAssembly> <assemblyIdentity name="System.Data.SqlServerCe" publicKeyToken="89845dcd8080cc91" /> <bindingRedirect oldVersion="4.0.0.0" newVersion="4.0.0.1" /> </dependentAssembly> </assemblyBinding> </runtime> </configuration>
SQLServerCe4アセンブリファイルの配置
Expresswebにはインストールされていないようなので、アセンブリファイル一式もアップロードしてやる必要がある。
<SQLServerCe4のインストールフォルダ>/private フォルダ以下すべてのファイルをMVCプロジェクトのbinフォルダにコピーする。
ファイルのプロパティ(ビルドアクション,ディレクトリにコピー)は特に変更していない。
配置後作業
アプリケーションデプロイ後jに *sdfのパーミッション設定を行う必要がある。
ブラウザからsdfファイルへアクセスはできなさそうなので、とりあえずはこれでよしとする。
TrueCryptでマウント時にフォルダ共有したい
マウント時に行う処理を指定するといったことが、TrueCryptではできなさそうなので、
バッチファイルを作成し、それを実行させることで対応した。
マウント&共有フォルダ作成
<...>は各自環境に置換してください。
cd <ボリュームファイルのフォルダ> "<TrueCrypt.exeフルパス>" /v <ボリュームファイル> /l D\ /a /p "<パスワード>" /q net share <共有名>=<共有フォルダパス> /grant:everyone,full
アンマウント
アンマウントすると共有解除されるっぽい。
"<TrueCrypt.exeフルパス>" /d D /q
レガシーコード改善ガイド5
このメソッドをテストハーネスで動かすことができません
隠れたメソッド
privateなメソッドをテストしたい場合の対処方法。
・publicなメソッドにしてしまう。*1
・そのメソッドの責務を別クラスに分離する。
・テストクラスからそのメソッドを呼び出せるようにリファクタリングする。
→ publicにすることもできない。依存性が複雑に絡まっているのでクラスの分離も納期的に難しい。 場合の最終的な方法である。
※テストが行い易いように設計する。テストが容易な設計は良い設計である。
//「テストクラスからそのメソッドを呼び出せるようにリファクタリングする。」 サンプル //元クラス public class CCAImage { //テスト対象なメソッド private void setSnapRegion(int x, int y, int dx, int dy){ ... } public void snap(){ ... } ... } //テストクラス public class TestingCCAImage : CCAImage { //元クラスのテスト対象メソッドをprotectedに変更し、それをオーバーライド。 protected override void setSnapRegion(int x, int y, int dx, int dy){ vase.setSnapRegion(...); } ... }
言語の「便利」機能
「sealedなクラスが絡んでいるレガシーコードをテスト可能にする」ケース
//本書より参照。 //HttpFileCollection(http://goo.gl/yVXB1), //HttpPostedFile(http://goo.gl/ZrQsw) は.NET標準クラス。sealedクラス且つpublicなコンストラクタが存在しない。 public class TestTarget { public IList getKSRStreams(HttpFileCollection files){ ArrayList list = new ArrayList(); foreach(string name in files){ HttpPostedFile file = files[name]; if(file.FileName.EndsWith(".ksr") || (file.FileName.EndsWith(".txt") && file.ContentLength > MIN_LEN)) { ... list.Add(file.InputStream); } } return list; } }
1,HttpFileCollectionについて調査
・クラス階層: NameObjectCollectionBase抽象クラスを継承している。
・filesの処理: foreachループに使用。
2,HttpPostedFileについて調査
・クラス階層: 自分がルートなsealedなクラス。
・fileの処理: FileName、ContentLength、InputStreamプロパティを呼び出して値取得を行う。
//テストが書けるように対応後 public class OurHttpFileCollection : NameObjectCollectionBase { ... } public interface IHttpPostedFile { string FileName(); int ContentLength(); Stream InputStream(); } public class HttpPostedFileWrapper : IHttpPostedFile { private HttpPostedFile source; public HttpPostedFileWrapper(HttpPostedFile source){ this.source = source; } public string FileName(){ get{return source.FileName;} } public int ContentLength(){ get{return source.ContentLength;} } public Stream InputStream(){ get{return source.InputStream;} } } public class FakeHttpPostedFile : IHttpPostedFile { public FakeHttpPostedFile(string fileName, string contentLength, Stream inputStream){ this.FileName = filename; this.InputStream= inputStream; this.contentLength = contentLength; } public string FileName(){ private set; get; } public int ContentLength(){ private set; get; } public Stream InputStream(){ private set; get; } } public class TestTarget { public IList getKSRStreams(OurHttpFileCollection files){ ArrayList list = new ArrayList(); foreach(string name in files){ IHttpPostedFile file = files[name]; if(file.FileName.EndsWith(".ksr") || (file.FileName.EndsWith(".txt") && file.ContentLength > MIN_LEN)) { ... list.Add(file.InputStream); } } return list; } }
*1:そのメソッドがクラスの状態に関わる場合は、publicにしてはいけない。
レガシ―コード改善ガイド4
このクラスをテストハーネスに入れることができません
いらただしいパラメータ
本番コードでは、それ以外に手段がない場合を除き、パラメーターにnullを渡すのはやめる。
nullを渡せるような実装だと、処理の至る所でパラメーターチェック処理が行われているはず。これはまずい。(可読性の問題)
代わりにNullオブジェクトパターンの適用を検討すべし。テストケースではnullを渡してもOK。
いらだたしいグローバルな依存関係
Singletonなクラスを内部で使用しているクラスに対するテスト。
→"Singletonなクラスの制約を緩和する" という発想があまり理解できない。
テスト用の処置ならわかるが、本番でもグローバル変数としてSingletonクラスを使用するのもOK!と書かれているように思える。
グローバルな変数を使用したい→Singletonクラスを作成!という判断に自分がどうしてもならないからか?
紹介されていることをソースに落とすとこんな感じでしょうか?
//シングルトンクラスのインターフェイス public interface ISingleton { int GetHogegeInt(); ... } //シングルトンクラス public class HogeSingleton : ISingleton { private static ISingleton instance = null; protected HogeSingleton(){} //言語によっては、アクセス制限をかけることができる。("テストクラスのアセンブリからのみアクセス可"みたいな感じ) public static void SetTestingInstance(ISingleton newInstance) { instance = newInstance; } public static ISingleton getInstance() { if(instance == null){ instance = new HogeSingleton(); return instance; } public int GetHogegeInt() { ... } .... } //テスト用のシングルトンクラス public SingletonForTest : ISingleton { public SingletonForTest() { ... } public int GetHogegeInt() { ... } ... } //内部でシングルトンクラスオブジェクトを使用しているクラス public class UseSingletonProc { public UseSingletonProc() { int hogeInt = HogeSingleton.GetInstance().GetHogegeInt(); ... } public bool ExistValue() { ... } .... } //テストクラス public class ATestClass { ... //テストケース public void TestCase() { HogeSingleton.SetTestingInstance(new SingletonForTest()); var obj = new UseSingletonProc(); Assert.AreEqure(true, obj.ExistValue()); ... } ... }
*追記
「いらだたしいグローバルな依存関係」の対応例について、自分なら下のような対応を行う。
「staticなクラスが何かのインターフェイスを実装する」というのを見たことが無いので、落ち着かない。
interface IHogege{ ... } class Hogege : IHogege {...} class TestingHogege : IHogege{...} class HogegeFactory { private static HogegeFactory instance; public static HogegeFactory CreateInstance(){ ...} public void SetHogegeType(Type hogegeType){...} public IHogege Create(...){} }
レガシーコード改善ガイド3
いつまで経っても変更作業が終わりません
遅延時間
外部ライブラリなどの依存性がひどいシステムのコードは、ビルドする度に未変更箇所すべてもビルドしなおす必要がある。
その間ぼーっと待つことになる。(遅延時間) 改善済コードだとビルド~テスト実行の時間が短いので、即座にフィードバックが得られる。
ビルドの依存関係の排除
あるクラスをテストコード上でインスタンス生成できても、まだ依存関係は存在する。
それはそのクラスオブジェクトを使用するすべてのクラスである。
この依存関係を排除するためには、クラスのインターフェイスを分離する必要がある。
→ publicな操作をInterfaceに移動し、クラスはそれを実装する。
このときにインターフェイスと実装クラスは別プロジェクトに分けること!それをしないとこの変更の意味は半減してしまう!
依存関係逆転の法則(Dependency Inversion Principle : DIP)
インターフェイスに依存する場合、その依存は通常、とても小さく目立たないものです。
インターフェイスを変更しなければ、コードを変更する必要はありません。そして、インターフェイスを変更することは、背後にある実装コードの変更に比べると、はるかに稀です。
インターフェイスがあれば、そのインターフェイスお使用するコードに影響を及ぼすことなく、インターフェイスの実装クラスを編集したり、新しい実装クラスを追加したりすることができます。
このような理由により、具象クラスに依存するよりも、インターフェイスや抽象クラスに依存するほうが優れています。
変更の少ないものに依存することで、ある変更が巨大なコンパイルを引き起こしてしまう可能性を小さくすることができます。
どうやって機能を追加すればよいのでしょうか?
テスト駆動開発
1,失敗するテストケースを記述する。*1
2,コンパイルする。
3,ビルドエラーになるので、テスト対象のメソッドを追加する。戻り値(がある場合)は適当なものを返すようにする。
4,テスト実行する。失敗することを確認。
5,テスト対象のメソッド実装する。
6,テスト実行する。成功することを確認。
7,重複を確認。必要であればリファクタリングを行う。テスト実行を行い、成功することを確認する。*2
を繰り返す。
差分プログラミング
機能追加を行う度にサブクラスを作成すると、追加した複数の機能に対応させたい要件が発生した場合に困ったことになる。その場合の対応。
最も安直な改善方法の一例として、コンストラクタにパラメーターを渡し、有効機能の切り替えを行うというものが考えられるが、パラメーターが大量に増えた場合に複雑なコードと化するであろう。
これに対応するには、切り替えが必要な操作を別クラスに分離するという方法が対応策の1つとして考えられる。
リスコフの置換原則(LSP) あるクラスのサブクラスはベースクラスと同様に扱うことができること。利用者側からしたら親子の関係なく使用する。 この原則を違反しないように気をつけることとしては、 1,親クラス(具象クラス)の具象メソッドをオーバーライドしない。 2,オーバーライドする必要がある場合は、親クラスメソッドが呼び出せるようにする。 原則に従っているクラス構成を「正規化された階層」と呼ぶ。