6

創建視圖模型時,可以將選項(例如,在下拉列表中使用)填充到視圖模型的setter屬性中。 問題是,當視圖模型稍後作爲參數(通過框架!)傳遞給操作方法時,這些屬性值並未自動重新填充 ,所以如果由於驗證錯誤需要重新顯示錶單,那麼您需要重新填充這些選項。用作Action方法參數的View Model實例的構造函數注入

我在這個問題中特別要求的一個潛在解決方案是如何使MVC框架用構造函數注入實例化視圖模型,這將爲視圖模型構造函數提供某種數據訪問對象的實現(例如存儲庫),可以用於檢索視圖請求的選項(例如,在助手方法「DropDownListFor」中)?

我認爲這個解決方案可能與IModelBinderProvider或IModelBinder的實現有關,但是在網絡的這些地方和那裏的示例代碼片段中嘗試了這些東西之後,我仍然在尋找一個完全可行的示例,代碼沒有任何缺失如何把所有東西放在一起。

如果您正在尋找關於如何填充選擇列表的一些備選討論,與「Dependecy查找」,而不是「Dependecy注入」你可能想看看下面的討論:填充的SelectList的視圖模型上GET 最佳方式/ POST Best way to populate SelectList for ViewModel on GET/POST

幾天前我寫了下面的後續向上在該線程有關「Dependecy注入」的問題,我現在在這個線程尋找: https://stackoverflow.com/a/8674525/310457 (它提供了有關我要尋找的解決該問題的代碼示例)

但是,相反的希望有人找到一個不太具體的標題的舊線程,我已經創建了一個關於我正在尋找的更具體主題的新問題河 而且我還會爲那些想要跟進這個特定解決方案的人提供一個從該主題到這個新問題的鏈接。

回答

5

我假設你想讓你的ViewModel通過它們的構造函數自動注入某些東西 - 例如某種配置對象,視圖將使用它來確定顯示內容。我還假設,當MVC嘗試從Controller Action的參數中自動創建並綁定模型實例時,此方法會導致「無參數定義的此對象的構造函數」錯誤。我們還假設我們將使用DI框架在運行時自動將SiteConfig對象注入到控制器中。

這意味着我們必須解決的唯一問題是如何從Controller中將注入的對象自動綁定到Actions的ViewModel中。

所以讓我們來定義一個基礎模型,讓其他人從中繼承。

BaseViewModel

public class BaseViewModel 
{ 
    public ISiteConfig SiteConfig { get; set; } 

    public BaseViewModel(ISiteConfig siteConfig) 
    { 
     this.SiteConfig = siteConfig; 
    } 
} 

現在讓我們創建一個從它繼承的典範。

IndexViewModel

public class IndexViewModel : BaseViewModel 
{ 
    public string SomeIndexProperty { get; set; } 

    public IndexViewModel (ISiteConfig siteConfig) : base(siteConfig) {} 
} 

現在讓我們定義一個基本的控制器,我們的控制器將繼承。

BaseController

public abstract class BaseController : Controller 
{ 
    protected BaseController(ISiteConfig siteConfig) 
    { 
     _siteConfig = siteConfig; 
    } 

    private readonly ISiteConfig _siteConfig; 

    public ISiteConfig SiteConfig 
    { 
     get 
     { 
      return _siteConfig; 
     } 
    } 
} 

現在我們定義我們的實際控制人。

的HomeController

public HomeController: BaseController 
{ 
    public HomeController(ISiteConfig siteConfig): base(siteConfig) {} 
} 

假設我們使用Ninject爲DI,Ninject將被配置爲自動創建控制器並在運行時通過一個具體的ISiteConfig對象到其構造函數。

現在我們將我們的動作添加到控制器。

索引操作

public ActionResult Index(IndexViewModel model) 
{ 
    return View(model); 
} 

所以這是在沒有做別的事情,MVC將與「無參數的構造函數」的錯誤,如果你嘗試調用索引操作引爆點,因爲MVC可以」找到一個不帶參數的ViewModel構造函數。

等等答案。我們需要重寫默認的ModelBinder。

BaseViewModelBinder

public class BaseViewModelBinder : DefaultModelBinder 
{ 
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) 
    { 
     if (modelType == typeof(BaseViewModel) || modelType.IsSubclassOf(typeof(BaseViewModel))) 
     { 
      var baseControl = controllerContext.Controller as BaseController; 
      if (baseControl == null) 
      { 
       throw new Exception("The Controller must derive from BaseController"); 
      } 

      var instance = Activator.CreateInstance(modelType, baseControl.SiteConfig); 
      bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => instance, modelType); 
      return instance; 
     } 
     else 
     { 
      return base.CreateModel(controllerContext, bindingContext, modelType); 
     } 
    } 
} 

我們需要global.asax.cs到其設置爲默認的模型綁定:

protected void Application_Start() 
{ 
    ... 
    ModelBinders.Binders.DefaultBinder = new BaseViewModelBinder(); 
} 

這就是全部。如您所見,當您現在查看索引操作時,MVC將使用我們的自定義模型綁定器。它會意識到IndexViewModel派生自BaseViewModel,因此會嘗試使用它可以在Action的Controller中找到的ISiteConfig來旋轉一個IndexViewModel實例(因爲Controller派生自BaseController)。

+0

我不明白ISiteConfig是什麼。我必須執行它嗎? – 2017-05-09 17:21:25

+0

ISiteConfig是您希望被自動注入到視圖中的SiteConfig對象的接口。 ISiteConfig和SiteConfig引用了你自己製作的配置對象 - 無論你想提供給你的視圖,只是例如。 – 2017-05-12 10:32:14

相關問題