如何在 ASP.NET MVC 2 中将 DI / IoC 容器与模型绑定器一起使用?
假设我有一个 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 和两个 Create 方法(一种用于 GET,一种用于 POST)。 GET 的方法是直接的,但 POST 的方法则不那么直接,也不那么简单:P 因为我需要与模型绑定程序搞乱,告诉它获取 ITimeProvider 实现的引用,以便能够构造一个用户实例。
public class UserController : Controller {
[HttpGet]
public ViewResult Create() {
return View();
}
[HttpPost]
public ActionResult Create(User user) {
// ...
}
}
我还希望能够保留默认模型活页夹的所有功能。
有机会解决这个简单/优雅/等问题吗? :D
Let's say I have an User entity and I would want to set it's CreationTime property in the constructor to DateTime.Now. But being a unit test adopter I don't want to access DateTime.Now directly but use an 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; } }
}
I am using NInject 2 in my ASP.NET MVC 2.0 application. I have a UserController and two Create methods (one for GET and one for POST). The one for GET is straight forward but the one for POST is not so straight and not so forward :P because I need to mess with the model binder to tell it to get a reference of an implementation of ITimeProvider in order to be able to construct an user instance.
public class UserController : Controller {
[HttpGet]
public ViewResult Create() {
return View();
}
[HttpPost]
public ActionResult Create(User user) {
// ...
}
}
I would also like to be able to keep all the features of the default model binder.
Any chance to solve this simple/elegant/etc? :D
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一些观察结果:
不要注入依赖项只是为了在构造函数中查询它们
没有理由将 ITimeProvider 注入用户只是为了立即调用
Now
。只需直接注入创建时间即可:与 DI 相关的一个非常好的经验法则是构造函数不应执行任何逻辑。
不要将 DI 与 ModelBinder 结合使用
ASP.NET MVC ModelBinder 是一个非常不适合进行 DI 的地方,特别是因为您无法使用构造函数注入。唯一剩下的选项是静态服务定位器反模式。
ModelBinder 将 HTTP GET 和 POST 信息转换为强类型对象,但从概念上讲,这些类型不是域对象,但类似于 数据传输对象。
ASP.NET MVC 的一个更好的解决方案是完全放弃自定义 ModelBinder,而是明确接受从 HTTP 连接收到的内容不是完整的域对象。
您可以使用简单的查找或映射器来检索控制器中的域对象:
其中
this.userRepository
是注入的依赖项。A couple of observations:
Don't inject dependencies just to query them in the constructor
There's no reason to inject an ITimeProvider into a user just to invoke
Now
immediately. Just inject the creation time directly instead:A really good rule of thumb related to DI is that constructors should perform no logic.
Don't use DI with ModelBinders
An ASP.NET MVC ModelBinder is a really poor place to do DI, particularly because you can't use Constructor Injection. The only remaining option is the static Service Locator anti-pattern.
A ModelBinder translates HTTP GET and POST information to a strongly typed object, but conceptually these types aren't domain objects, but similar to Data Transfer Objects.
A much better solution for ASP.NET MVC is to forego custom ModelBinders completely and instead explicitly embrace that what you receive from the HTTP connection is not your full domain object.
You can have a simple lookup or mapper to retrieve your domain object in your controller:
where
this.userRepository
is an injected dependency.不如使用
ITimeProvider
试试这个:在你的单元测试中:
我知道这不是很优雅,但这可能是一个解决方案,而不是弄乱模型绑定器。如果这不是一个好的解决方案,您可以实现自定义模型绑定器并覆盖 CreateModel 方法,您可以在模型的构造函数中注入依赖项。
How about instead of using an
ITimeProvider
try this:And in your unit test:
I know that this is not very elegant but instead of messing with the model binder this could be a solution. If this doesn't feel like a good solution you could implement a custom model binder and override the CreateModel method where you would inject the dependencies in the constructor of the model.
另一种选择是创建一个不同的类来表示尚未保留且根本不具有创建日期属性的用户。
即使
CreationDate
是User
的不变量之一,它在视图模型中也可以为空 - 并且您可以在更下游的控制器或域层中将其设置。毕竟,这可能并不重要,但创建日期属性是否真正代表您构建用户实例的时刻,或者它更适合代表用户提交数据的时刻?
Another option is to create a different class to represent users that haven't been persisted yet that doesn't have a creation date property at all.
Even if
CreationDate
is one ofUser
's invariants, it can be nullable in your view model - and you can set it farther downstream, in your controller or domain layer.After all, it probably doesn't matter, but should the creation date attribute really represent the moment you construct a user instance, or would it be more appropriate for it to represent the moment a user submits their data?