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

Solution 1:

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.

Solution 2:

How about instead of using an ITimeProvider try this:

public class User 
{
    public Func<DateTime> DateTimeProvider = () => DateTime.Now;

    public User() 
    {
        this.CreationTime = DateTimeProvider();
    }
}

And in your unit test:

var user = new User();
user.DateTimeProvider = () => new DateTime(2010, 5, 24);

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.

Solution 3:

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 of User'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?