如何在 ASP.NET MVC 2+ 中將 DI / IoC 容器與模型綁定器一起使用?
假設我有一個使用者實體,我想在建構子中將它的 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
不如
ITimeProvider試試這個:public class User { public Func<DateTime> DateTimeProvider = () => DateTime.Now; public User() { this.CreationTime = DateTimeProvider(); } }在你的單元測試中:
var user = new User(); user.DateTimeProvider = () => new DateTime(2010, 5, 24);我知道這不是很優雅,但與其弄亂模型綁定器,這可能是一個解決方案。如果這不是一個好的解決方案,您可以實現自定義模型綁定器並覆蓋CreateModel方法,您將在模型的建構子中註入依賴項。
幾點觀察:
不要注入依賴項只是為了在建構子中查詢它們
沒有理由將 ITimeProvider 注入使用者只是為了
Now立即呼叫。只需直接注入創建時間:public User(DateTime creationTime) { this.CreationTime = creationTime; }與 DI 相關的一個非常好的經驗法則是建構子不應該執行任何邏輯。
不要將 DI 與 ModelBinders 一起使用
ASP.NET MVC ModelBinder 是執行 DI 的一個非常糟糕的地方,特別是因為您不能使用建構子注入。唯一剩下的選項是靜態服務定位器反模式。
ModelBinder 將 HTTP GET 和 POST 資訊轉換為強類型對象,但從概念上講,這些類型不是域對象,而是類似於Data Transfer Objects。
一個更好的 ASP.NET MVC 解決方案是完全放棄自定義 ModelBinders,而是明確接受從 HTTP 連接接收到的不是完整的域對象。
您可以使用簡單的查找或映射器來檢索控制器中的域對象:
public ActionResult Create(UserPostModel userPost) { User u = this.userRepository.Lookup(userPost); // ... }
this.userRepository注入的依賴項在哪裡。