Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
672 views
in Technique[技术] by (71.8m points)

dependency injection - How to use a DI / IoC container with the model binder in ASP.NET MVC 2+?

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

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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:

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

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:

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

where this.userRepository is an injected dependency.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...