Wie ein DI / IoC-Container mit dem Modell Bindemittel in ASP.NET MVC 2+ benutzen?
Frage
Lassen Sie sich sagt, dass ich einen Benutzer Einheit habe und ich würde es die Eigenschaft Creation im Konstruktor DateTime.Now festlegen möge. Aber ein Unit-Test ist adopter Ich möchte nicht den Zugriff auf DateTime.Now direkt, sondern eine ITimeProvider verwenden:
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; } }
}
Ich bin mit ninject 2 in meiner ASP.NET MVC 2.0-Anwendung. Ich habe einen Usercontroller und zwei erstellen Methoden (eine für GET und eine für POST). Die ein für GET ist gerade nach vorne, aber die für POST ist nicht so gerade und nicht so vorwärts: P, weil ich mit dem Modell Bindemittel zu verwirren muss ihm sagen, eine Referenz für eine Implementierung von ITimeProvider zu erhalten, um zu konstruieren zu können, eine Benutzerinstanz.
public class UserController : Controller {
[HttpGet]
public ViewResult Create() {
return View();
}
[HttpPost]
public ActionResult Create(User user) {
// ...
}
}
Ich möchte auch alle die Eigenschaften des Standardmodells Bindemittel halten können.
Jede Chance, diesen einfachen / elegant / etc zu lösen? : D
Lösung
Wie wäre es stattdessen eine ITimeProvider
der Verwendung versuchen Sie dies:
public class User
{
public Func<DateTime> DateTimeProvider = () => DateTime.Now;
public User()
{
this.CreationTime = DateTimeProvider();
}
}
Und in Ihrem Gerät zu testen:
var user = new User();
user.DateTimeProvider = () => new DateTime(2010, 5, 24);
Ich weiß, dass dies nicht sehr elegant ist, sondern mit dem Modell Messing Binder dies eine Lösung sein könnte. Wenn dies wie eine gute Lösung nicht das Gefühl kann man ein eigenes Modell Bindemittel implementieren und die Create Verfahren, in dem Sie die Abhängigkeiten in dem Konstruktor des Modells injizieren würden.
Andere Tipps
Ein paar Beobachtungen:
Nicht injizieren Abhängigkeiten nur um sich im Konstruktor abfragen
Es gibt keinen Grund, eine ITimeProvider in einem Benutzer zu injizieren nur Now
sofort aufzurufen. injiziert nur die Erstellungszeit direkt statt:
public User(DateTime creationTime)
{
this.CreationTime = creationTime;
}
Eine wirklich gute Faustregel gilt: DI Zusammenhang ist, dass die Konstrukteure keine Logik durchführen sollte .
Verwenden DI nicht mit Modelbinder
Eine ASP.NET MVC ist ein Modelbinder wirklich schlecht Ort DI zu tun, vor allem, weil Sie nicht Constructor Injection verwenden können. Die einzige verbleibende Option ist das statische Service Locator anti-Muster .
Ein Modelbinder übersetzt HTTP GET und POST-Informationen zu einem stark typisierte Objekt, aber konzeptuell Diese Typen sind nicht Domain-Objekte, aber ähnlich wie Datenübertragung Objekte .
Eine viel bessere Lösung für ASP.NET MVC ist benutzerdefinierten Modelbinder vollständig zu verzichten und stattdessen explizit umarmt das, was Sie von der HTTP-Verbindung empfangen ist nicht Ihr vollständiges Domain-Objekt .
Sie können eine einfache Lookup oder Mapper, um Ihre Domain-Objekt in Ihrem Controller abrufen:
public ActionResult Create(UserPostModel userPost)
{
User u = this.userRepository.Lookup(userPost);
// ...
}
Dabei gilt this.userRepository
eine injiziert Abhängigkeit.
Eine weitere Möglichkeit ist eine andere Klasse erstellen Benutzer darzustellen, die nicht gewesen sein beharrten noch nicht ein Erstellungsdatum Eigentum überhaupt nicht hat.
Auch wenn CreationDate
eine von User
der Invarianten, kann es Ihrer Ansicht nach Modell nullable sein -. Und Sie können einstellen, es weiter stromabwärts, in dem Controller oder Domänenschicht
Schließlich tut es wahrscheinlich keine Rolle, sollte aber das Erstellungsdatum Attribut wirklich den Moment stellen Sie eine Benutzerinstanz konstruieren , oder wäre es sinnvoller sein, für den Moment, für einen Benutzer trägt ihre Daten?