ASP.NET MVC 2+でモデルバインダーを備えたDI / IOCコンテナを使用する方法は?
質問
ユーザーエンティティがあり、コンストラクターにdateTime.now.nowのCreationTimeプロパティを設定したいとします。ただし、ユニットテストの採用者であるため、DateTime.now.nowは直接アクセスしたくありませんが、itimeproviderを使用します。
public class User {
public User(ITimeProvider timeProvider) {
// ...
this.CreationTime = timeProvider.Now;
}
// .....
}
public interface ITimeProvider {
public DateTime Now { get; }
}
public class TimeProvider : ITimeProvider {
public DateTime Now { get { return DateTime.Now; } }
}
ASP.NET MVC 2.0アプリケーションでNINJECT 2を使用しています。 usercontrollerと2つの作成方法(1つはget用、もう1つは投稿用)があります。 GETのためのものは簡単ですが、投稿用のものはそれほどまっすぐではなく、それほど前方ではありません。Pは、モデルバインダーを台無しにして、itimeproviderの実装の参照を取得するために指示する必要があるため、ユーザーインスタンス。
public class UserController : Controller {
[HttpGet]
public ViewResult Create() {
return View();
}
[HttpPost]
public ActionResult Create(User user) {
// ...
}
}
また、デフォルトのモデルバインダーのすべての機能を維持できるようにしたいと考えています。
このシンプル/エレガント/などを解決するチャンスはありますか? :d
解決
使用する代わりに ITimeProvider
これを試して:
public class User
{
public Func<DateTime> DateTimeProvider = () => DateTime.Now;
public User()
{
this.CreationTime = DateTimeProvider();
}
}
そしてあなたのユニットテストで:
var user = new User();
user.DateTimeProvider = () => new DateTime(2010, 5, 24);
これはあまりエレガントではありませんが、モデルバインダーをいじるのではなく、これが解決策になる可能性があることを知っています。これが良いソリューションのように感じられない場合は、カスタムモデルバインダーを実装してオーバーライドできます createModel モデルのコンストラクターに依存関係を注入する方法。
他のヒント
いくつかの観察:
コンストラクターで照会するためだけに依存関係を注入しないでください
呼び出すためだけにitimeproviderをユーザーに挿入する理由はありません Now
すぐに。代わりに作成時間を直接注入するだけです。
public User(DateTime creationTime)
{
this.CreationTime = creationTime;
}
DIに関連する本当に良い経験則は コンストラクターはロジックを実行してはなりません.
ModelBindersでDIを使用しないでください
ASP.NET MVC Modelbinderは、特にコンストラクターインジェクションを使用できないため、DIを行うのに非常に貧弱な場所です。残りの唯一のオプションは次のとおりです 静的サービスロケーターアンチパターン.
ModelBinderはHTTP GETと情報を強くタイプされたオブジェクトに翻訳しますが、 概念的に これらのタイプはドメインオブジェクトではありませんが、に似ています データ転送オブジェクト.
ASP.NET MVCのはるかに優れたソリューションは、カスタムモデルバインダーを完全に、代わりに放棄することです。 明示的に受け入れます HTTP接続から受け取るものはそうです フルドメインオブジェクトではありません.
コントローラー内のドメインオブジェクトを取得するための簡単なルックアップまたはマッパーを使用できます。
public ActionResult Create(UserPostModel userPost)
{
User u = this.userRepository.Lookup(userPost);
// ...
}
どこ this.userRepository
注入された依存関係です。
もう1つのオプションは、まだ持続していないユーザーを表現するために、別のクラスを作成することです。
もしそれでも CreationDate
の一つであります User
variantsは、ビューモデルでは無視できます。コントローラーまたはドメインレイヤーで、下流にさらに設定できます。
結局のところ、それはおそらく問題ではありませんが、作成日属性が本当にあなたの瞬間を表している場合は ユーザーインスタンスを作成します, 、または、ユーザーがデータを提出する瞬間を表す方が適切でしょうか?