¿Cómo usar un contenedor DI / IoC con la carpeta modelo en ASP.NET MVC 2+?

Digamos que tengo una entidad de usuario y me gustaría establecer su propiedad CreationTime en el constructor en DateTime.Now. Pero al ser un adoptador de pruebas unitarias, no quiero acceder a DateTime.Now directamente, pero uso un 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; } } } 

Estoy usando NInject 2 en mi aplicación ASP.NET MVC 2.0. Tengo un UserController y dos métodos Create (uno para GET y otro para POST). El de GET es directo, pero el de POST no es tan directo y no es tan avanzado: P porque tengo que meterme con el encuadernador de modelos para decirle que obtenga una referencia de una implementación de ITimeProvider para poder construir una instancia de usuario.

 public class UserController : Controller { [HttpGet] public ViewResult Create() { return View(); } [HttpPost] public ActionResult Create(User user) { // ... } } 

También me gustaría poder mantener todas las características de la carpeta de modelo predeterminada.

¿Alguna posibilidad de resolver esto simple / elegante / etc.? :RE

¿Qué tal si en lugar de usar un ITimeProvider prueba esto:

 public class User { public Func DateTimeProvider = () => DateTime.Now; public User() { this.CreationTime = DateTimeProvider(); } } 

Y en tu prueba de unidad:

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

Sé que esto no es muy elegante, pero en lugar de meterse con la carpeta de modelos, esta podría ser una solución. Si esto no se siente como una buena solución, podría implementar un encuadernador de modelo personalizado y anular el método CreateModel donde se inyectarían las dependencias en el constructor del modelo.

Un par de observaciones:

No inyecte dependencias solo para consultarlas en el constructor

No hay ninguna razón para inyectar un ITimeProvider en un usuario solo para invocar Now inmediatamente. Simplemente inyecte el tiempo de creación directamente en su lugar:

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

Una buena regla general relacionada con DI es que los constructores no deben realizar ninguna lógica .

No use DI con ModelBinders

Un ASP.NET MVC ModelBinder es un lugar realmente pobre para hacer DI, particularmente porque no puede usar Constructor Injection. La única opción restante es el anti-patrón de Localizador de Servicio estático .

Un ModelBinder traduce la información HTTP GET y POST a un objeto con fuerte tipado, pero conceptualmente estos tipos no son objetos de dominio, sino que son similares a los objetos de transferencia de datos .

Una solución mucho mejor para ASP.NET MVC es renunciar completamente a ModelBinders personalizados y, en su lugar, abrazar explícitamente que lo que recibe de la conexión HTTP no es su objeto de dominio completo .

Puede tener una búsqueda simple o mapeador para recuperar su objeto de dominio en su controlador:

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

donde this.userRepository es una dependencia inyectada.

Otra opción es crear una clase diferente para representar a los usuarios que aún no se han conservado pero que no tienen ninguna propiedad de fecha de creación.

Incluso si CreationDate es uno de los invariantes del User , puede ser anulable en su modelo de vista, y puede establecerlo más adelante, en su controlador o capa de dominio.

Después de todo, probablemente no importe, pero ¿el atributo de fecha de creación realmente representa el momento en que construyes una instancia de usuario , o sería más apropiado que represente el momento en que el usuario envía sus datos?