8

假设我有一个用户实体,我想在构造函数中将它的 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 和两个 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 相关的一个非常好的经验法则是构造函数不应该执行任何逻辑

不要将 DI 与 ModelBinders 一起使用

ASP.NET MVC ModelBinder 是执行 DI 的一个非常糟糕的地方,特别是因为您不能使用构造函数注入。唯一剩下的选项是静态服务定位器反模式

ModelBinder 将 HTTP GET 和 POST 信息转换为强类型对象,但从概念上讲,这些类型不是域对象,但类似于Data Transfer Objects

一个更好的 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

另一种选择是创建一个不同的类来表示尚未持久化且根本没有创建日期属性的用户。

即使CreationDateUser的不变量之一,它也可以在您的视图模型中为空 - 您可以将其设置在更下游,在您的控制器或域层中。

毕竟,这可能无关紧要,但是创建日期属性应该真正代表您构建用户实例的时刻,还是更适合代表用户提交数据的时刻?

于 2010-05-24T20:07:26.797 回答