Как получить данные для выпадающего списка в viewmodel при использовании AutoMapper / AutoMapViewResult - PullRequest
10 голосов
/ 18 августа 2010

После прочтения ASP.NET MVC 2 в действии и просмотра презентации Джимми Богарда из MvcConf (оба настоятельно рекомендуется!) Я начал реализовывать некоторые из их идей.

Одна из замечательных вещей, которые они делают, это не только использовать 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 'Locations?

Имейте в виду, что я хочу использовать AutoMapViewResult и множество различных комбинаций сущность-модель представления.

Обновление:

Я пошел с идеей Некроса и создал собственный атрибут. Вы можете посмотреть код и загрузить его в моем блоге ASP.NET MVC: загрузка данных для выбранных списков в модель редактирования с использованием атрибутов .

Ответы [ 4 ]

6 голосов
/ 07 июля 2011

Мое решение для этого состояло в том, чтобы ввести концепцию Обогатителей моделей, простых классов, которые «обогащают» модель перед ее передачей в 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);
    }

Поскольку я также использую FormActionResult из презентации Джимми, я также использую обогащение перед возвратом результата Failure.Это означает, что такие вещи, как списки выбора, переобвязаны и сохраняют вещи супер СУХИМ.

[Обновление]

Я разместил улучшенное решение здесь , основанное на вышеприведенном.

5 голосов
/ 18 августа 2010

Я не дошел до сути (с тех пор, как увидел разговор), когда мне это было нужно, но у меня есть возможное решение для этого. Я думаю, что это будет работать для создания атрибута, указывающего, что это свойство должно быть загружено. Я бы начал с абстрактного класса:

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, вызовите LoadData(), приведите его к типу, указанному в атрибуте, и назначьте его свойству.

В случае, если вы просто хотите загрузить списки выбора таким способом, вы можете просто вернуть IList<SelectListItem> вместо object и избавить себя от некоторых проблем с кастом.

Ваша модель представления, очевидно, будет использовать атрибут.

public class EventsEditModel
{
    // ... some properties ...
    public int LocationId { get; set; }

    [LoadLocations]
    public IList<SelectListItem> Locations { get; set; }
}
0 голосов
/ 18 августа 2010

Я бы порекомендовал вам взглянуть на asp.net-mvc sample application из здесь , что делает это намного проще, чем automaper

Особенно посмотрите на TinyController

0 голосов
/ 18 августа 2010

Ну, добавьте конструктор с параметром и свойством в ваш контроллер и используйте DI (лично мне нравится Ninject), чтобы внедрить правильную реализацию репозитория:

public IEventsRepository _repo;

public EventsController(IEventsRepository repository)
{
  _repo = repository;
}

Связать (связать) зависимости вверх вglobal.asax.cs в приложении и модуле сайта Ninject (если вам нужен расширенный ответ с включенным, пожалуйста, дайте мне знать),

, затем в вашем действии Edit используйте репозиторий, чтобы получить Locations.Предположим, у вас есть метод LoadLocations() в интерфейсе репозитория и его конкретная реализация, например, SqlEventsRepository (реализует IEventsRepository), вы делаете это просто вызывая метод:

public ActionResult Edit(Event id)
{
...
EventsEditModel model = new EventsEditModel();
_repo.GetInstance(id);
model.Locations = _repo.LoadLocations();
...
}

Я придумываю это, потому что вы не предоставили слишком много информации.И я не знаю особенностей Automapper, когда вы хотите загрузить некоторые дополнительные данные из хранилища данных до сопоставления сущности с ViewModel.

Также вы не указываете, является ли это действие редактирования GET или POST, но я предполагаю, что это GET.Предполагая, что это действительно GET, я не знаю, как вы можете загрузить что-либо, предоставив Entity для действия.

Чаще всего методы GET используют параметры типа string или int (скорее всего, те, чтоявляются слизнями или идентификаторами некоторого рода) и POST методы используют параметры типа ViewModel (не Entity).

Таким образом, ваша подпись метода POST должна быть такой:

[HttpPost]
public ActionResult Edit(EventsEditModel model)...

, которую я использовал для использованияСущности прямо в моих действиях подписывались и терпели неудачу все время, поэтому я не рекомендую это другим.

HTH

...