Asp.net-Mvc

使用 AutoMapper/AutoMapViewResult 時如何將下拉列表的數據獲取到視圖模型中

  • September 23, 2011

在閱讀了 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;
   }
}

我的 AutoMapperViewResultExecuteResult方法看起來像:

   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 $$

我在這裡發布了一個基於上述內容的改進解決方案。

引用自:https://stackoverflow.com/questions/3507466