使用 AutoMapper/AutoMapViewResult 時如何將下拉列表的數據獲取到視圖模型中
在閱讀了 ASP.NET MVC 2 in Action並觀看了來自 MvcConf 的 Jimmy Bogard 的展示文稿(強烈推薦!)之後,我開始實施他們的一些想法。
他們做的一件很酷的事情,不僅是使用 AutoMapper 將您的實體映射到某個視圖模型,而且使用 AutoMapViewResult 自動執行此操作:
public class EventsController : BaseController { public ActionResult Show(Event id) // EntityModelBinder gets Event from repository { return AutoMapView<EventsShowModel>(id); // AutoMapView<T>(model) is a helper method on the BaseController, that calls AutoMapViewResult<T>(...) } } // not exactly what you'll find in the book, but it also works :-) public class AutoMapViewResult<TDestination> : ViewResult { public AutoMapViewResult(string viewName, string masterName, object model) { ViewName = viewName; MasterName = masterName; ViewData.Model = Mapper.Map(model, model.GetType(), typeof(TDestination)); } }這一切都很好,但現在有一個
Edit動作EventsEditModel:public class EventsEditModel { // ... some properties ... public int LocationId { get; set; } public IList<SelectListItem> Locations { get; set; } } public class EventsController : BaseController { public ActionResult Edit(Event id) { return AutoMapView<EventsEditModel>(id); } }現在(最後)問題是:
您認為,將位置從某種數據源(例如儲存庫)獲取到
EventsEditModel’sLocations屬性的最佳方法是什麼?請記住,我想使用
AutoMapViewResult許多不同的實體視圖模型組合。更新:
我採用了 Necros 的想法並創建了一個自定義屬性。您可以查看程式碼並將其下載到我的部落格ASP.NET MVC: Loading data for select lists into edit model using attributes。
當我需要這個時,我還沒有達到目的(因為我看到了談話),但我有一個可能的解決方案。我認為創建一個屬性,指定需要載入這個屬性是可行的。我將從一個抽像類開始:
public abstract class LoadDataAttribute : Attribute { public Type Type { get; set; } protected LoadDataAttribute(Type type) { Type = type; } public abstract object LoadData(); }然後為您要載入的每種類型創建特定版本(在您的情況下為位置)
public class LoadLocationsAttribute : LoadDataAttribute { public LoadLocationsAttribute() : base(typeof(IList<SelectListItem>)) public override object LoadData() { // get locations and return IList<SelectListItem> } }在您
ExecuteResult中,AutoMappViewResult您會找到所有帶有LoadDataAttribute, callLoadData()的屬性,將其轉換為屬性中指定的類型並將其分配給屬性。如果您只想以這種方式載入選擇列表,您可以直接返回
IList<SelectListItem>而不是object,並為自己省去一些轉換的麻煩。您的視圖模型顯然會使用該屬性。
public class EventsEditModel { // ... some properties ... public int LocationId { get; set; } [LoadLocations] public IList<SelectListItem> Locations { get; set; } }
我對此的解決方案是引入模型豐富器的概念,即在模型傳遞給 View() 之前“豐富”模型的簡單類:
public class SiteSettingsModelEnricher : IModelEnricher<SiteSettingsModel> { private readonly IThemeProvider themeProvider; public SiteSettingsModelEnricher(IThemeProvider themeProvider) { this.themeProvider = themeProvider; } public SiteSettingsModel Enrich(SiteSettingsModel model) { var themes = from t in themeProvider.GetThemes() select new SelectListItem { Text = t, Value = t }; model.Themes = themes; return model; } }我的 AutoMapperViewResult
ExecuteResult方法看起來像:public override void ExecuteResult(ControllerContext context) { var model = Mapper.Map(this.Model, typeof(TSource), typeof(TDestination)) as TDestination; // enrichers var enricher = DependencyResolver.Current.GetService<IModelEnricher<TDestination>>(); if (enricher != null) { model = enricher.Enrich(model); } this.ViewData.Model = model; base.ExecuteResult(context); }由於我也在使用 Jimmy 展示文稿中的 FormActionResult,因此我在返回失敗結果之前也使用了豐富器。這意味著選擇列表之類的東西被重新綁定並保持超級乾燥。
$$ Update $$
我在這裡發布了一個基於上述內容的改進解決方案。