8

User エンティティがあり、コンストラクターでその CreationTime プロパティを DateTime.Now に設定するとします。しかし、単体テストの採用者であるため、 DateTime.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 つの Create メソッド (GET 用と POST 用) があります。GET 用のものは単純ですが、POST 用のものはそれほど単純ではなく、それほど前向きでもありません:P を構築できるようにするために、ITimeProvider の実装の参照を取得するようにモデル バインダーをいじる必要があるためです。ユーザー インスタンス。

public class UserController : Controller {

    [HttpGet]
    public ViewResult Create() {
         return View();
    }

    [HttpPost]
    public ActionResult Create(User user) {

         // ...

    }
}

また、デフォルトのモデル バインダーのすべての機能を保持できるようにしたいと考えています。

このシンプル/エレガント/その他を解決する機会はありますか? :D

4

3 に答える 3

20

いくつかの観察:

コンストラクターでクエリするためだけに依存関係を注入しないでください

すぐに呼び出すためだけに ITimeProvider をユーザーに挿入する理由はありませんNow。代わりに、作成時間を直接挿入するだけです。

public User(DateTime creationTime)
{
     this.CreationTime = creationTime;
}

DI に関連する非常に優れた経験則は、コンストラクターはロジックを実行してはならないということです。

ModelBinders で DI を使用しないでください

ASP.NET MVC ModelBinder は、特にコンストラクター インジェクションを使用できないため、DI を行うには非常に適していません。残っている唯一のオプションは、静的 Service Locator anti-patternです。

ModelBinder は HTTP GET および POST 情報を厳密に型指定されたオブジェクトに変換しますが、概念的にはこれらの型はドメイン オブジェクトではなく、データ転送オブジェクトに似ています。

ASP.NET MVC のはるかに優れたソリューションは、カスタム ModelBinders を完全に放棄し、代わりに、HTTP 接続から受け取るものが完全なドメイン オブジェクトではないことを明示的に受け入れることです。

簡単なルックアップまたはマッパーを使用して、コントローラーでドメイン オブジェクトを取得できます。

public ActionResult Create(UserPostModel userPost)
{
    User u = this.userRepository.Lookup(userPost);
    // ...
}

どこthis.userRepositoryに注入された依存関係があります。

于 2010-05-25T07:46:34.287 に答える
5

これを試してみる代わりにどうですか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メソッドをオーバーライドできます。

于 2010-05-24T19:35:09.947 に答える
1

もう 1 つのオプションは、作成日プロパティをまったく持たない、まだ永続化されていないユーザーを表す別のクラスを作成することです。

CreationDateが の不変条件の1 つである場合でもUser、ビュー モデルで null 可能にすることができます。さらに下流のコントローラーまたはドメイン レイヤーで設定できます。

結局のところ、おそらく問題ではありませんが、作成日属性は実際にユーザー インスタンスを作成した瞬間を表すべきでしょうか?それとも、ユーザーがデータを送信した瞬間を表す方が適切でしょうか?

于 2010-05-24T20:07:26.797 に答える