После прочтения ASP.NET MVC 2 в действии и просмотра презентации Джимми Богарда из MvcConf (оба настоятельно рекомендуется!), Я начал реализовывать некоторые из их идей.
Одна из замечательных вещей, которые они делают, - это не только использовать AutoMapper для сопоставления ваших сущностей с некоторой моделью представления, но и автоматизировать это с помощью AutoMapViewResult:
public class EventsController : BaseController
{
public ActionResult Show(Event id) // EntityModelBinder gets Event from repository
{
return AutoMapView(id); // AutoMapView(model) is a helper method on the BaseController, that calls AutoMapViewResult(...)
}
}
// not exactly what you'll find in the book, but it also works :-)
public class AutoMapViewResult : 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 Locations { get; set; }
}
public class EventsController : BaseController
{
public ActionResult Edit(Event id)
{
return AutoMapView(id);
}
}
И теперь (наконец) вопрос:
Что вы думаете, это лучший способ получить местоположения из какого-либо источника данных, такого как репозиторий, в свойство EventsEditModel
Locations ?
Имейте в виду, что я хочу использовать AutoMapViewResult
и множество различных комбинаций сущности-модели представления.
Обновление:
Я согласился с идеей Некроса и создал собственный атрибут. Вы можете посмотреть код и загрузить его в моем блоге ASP.NET MVC: загрузка данных для выбранных списков в модель редактирования с использованием атрибутов .
Я еще не дошел до того момента (с тех пор, как посмотрел доклад), когда мне это понадобится, но у меня есть возможное решение для этого. Я думаю, что было бы неплохо создать атрибут, указывающий, что это свойство должно быть загружено. Я бы начал с абстрактного класса:
public abstract class LoadDataAttribute : Attribute
{
public Type Type { get; set; }
protected LoadDataAttribute(Type type)
{
Type = type;
}
public abstract object LoadData();
}
Затем создал бы специальную версию для каждого типа, который вы хотите загрузить (Locations в вашем случае)
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
вместо object
и избавить себя от проблем с приведением.
Ваша модель представления, очевидно, будет использовать атрибут.
public class EventsEditModel
{
// ... some properties ...
public int LocationId { get; set; }
[LoadLocations]
public IList<SelectListItem> Locations { get; set; }
}
Я бы порекомендовал вам взглянуть на пример приложения asp.net-mvc
из здесь , которое делает это намного проще, чем автомобильная бумага
Особенно посмотрите на TinyController
Хорошо, добавьте конструктор с параметром и свойством в ваш контроллер и используйте DI (лично мне нравится Ninject) для внедрения правильной реализации репозитория:
public IEventsRepository _repo;
public EventsController(IEventsRepository repository)
{
_repo = repository;
}
Wire (привязка) зависимости вверху в 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, когда вы хотите загрузить некоторые дополнительные данные из хранилища данных до сопоставления Entity с ViewModel.
Также вы не указываете, является ли это действие редактирования GET
или POST
, но я предполагаю, что это GET
. Предполагая, что это действительно GET
, я не знаю, как вы можете загрузить что-нибудь, предоставив Entity действию.
Чаще всего методы GET
используют параметры типа string или int (скорее всего, это slugs или id некоторого вида), а методы POST
используют параметры типа ViewModel (не Entity) .
Итак, подпись метода POST должна быть такой.
[HttpPost]
public ActionResult Edit(EventsEditModel model)...
Я использовал Entities непосредственно в своих подписях действий и все время терпел неудачу, поэтому сейчас я не рекомендую это другим.
HTH