Модель ASP.NET MVC для отображения модели с дополнительными вторичными объектами
-
25-10-2019 - |
Вопрос
Я использую ASP.NET MVC 3, с Raven DB в качестве хранилища данных. У меня есть набор моделей, которые меня заинтересован в превращении в ViewModels. Чтобы сделать это, я использую Automapper, чтобы позаботиться о грязной работе по картированию каждого свойства с соответствующим в ViewModel. Допустим, у меня есть такая модель:
public class FooModel
{
public int Id { get; set; }
public string Name { get; set; }
public int AlphaId { get; set; }
public int BetaId { get; set; }
}
А потом скажем, что я хочу преобразовать его в ViewModel, как так:
public class FooViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public int AlphaId { get; set; }
public Alpha Alpha { get; set; }
public int BetaId { get; set; }
public Beta Beta { get; set; }
}
Моя карта затем настраивается так на стартапе приложения:
Mapper.CreateMap<Foo, FooViewModel>();
Затем в контроллере я выполняю карту так:
public ActionResult FooDetails(string id)
{
using(var session = this.documentStore.OpenSession())
{
var fooInstance = session.Load<Foo>(id);
var fooViewModel = Mapper.Map<FooViewModel>(fooInstance);
return this.View(fooViewModel);
}
}
Проблема в том, что, как вы можете видеть выше, сущность, выходящая из репозитория, имеет 2 свойства, которые являются ключами других объектов, типов альфа и бета. Я заинтересован в увлажнении альфа и бета -версии на основе Alphaid и Betaid Keys.
Сначала я подумал, что использую пользовательские возможности Automapper на заказ, но я не думаю, что это сработает, учитывая, что нам понадобится сеанс данных, который должен быть введен в картирование (чтобы вызвать хранилище данных, чтобы получить альфа или бета -объект) Анкет
Другой вариант - просто выполнить всю работу в действии контроллера, но это быстро становится громоздким (не в конкретном примере, но это всего лишь пример, чтобы проиллюстрировать эту точку зрения).
Где должна происходить гидратация альфа и бета -версию, и что здесь хорошая схема?
Решение
Возможно, это не идеально, но не могли бы вы просто создать карты для двух ссылок и отобразить их после факта. Вам все еще нужно позвонить им отдельно, но вы позволяете автоматическому картированию, а не наполнять свой контроллер своими картами.
public ActionResult FooDetails(string id)
{
using(var session = this.documentStore.OpenSession())
{
var foo = session.Load<Foo>(id).Include(....);
var alpha = session.Load<Alpha>(foo.AlphaId);
var beta = session.Load<Beta>(foo.BetaId);
// null checks
var fooViewModel = Mapper.Map<FooViewModel>(foo);
fooViewModel.Alpha = Mapper.Map<AlphaViewModel>(alpha);
fooViewModel.Beta = Mapper.Map<BetaViewModel>(beta);
return View(fooViewModel);
}
}
Я не использовал Automapper так много, но, возможно, вы можете пройти в детских объектах через функцию карты (чтобы питать цепочки формирования в CreateMap)? Возможно, предложение после карты Александра может сработать, но я не вижу, как вы увлажните «ссылочных» детей, не создавая карту внутри действия контроллера (но тогда вы можете просто использовать кадр и сеанс ворона напрямую и иметь только один Mapper.map).
Добавление - Документация Automapper предлагает аналогичный метод для вложенных/дочерних карт - https://github.com/automapper/automapper/wiki/nestest-mappings. Анкет Хотя это больше похоже на ссылку на объект, а не справочную ситуацию идентификатора объекта.
Я пока не могу комментировать другие ответы, но в отношении ответа Павела - вы на самом деле не используете ворон таким образом, нет картирования O/R (кроме внутреннего объекта JSON -> или уровня доступа к данным, Вы просто используете сеанс ворона напрямую (возможно, через сервис, но, конечно, нет необходимости в репозитории или аналогичном). Что касается ссылки, байт имеет это правильно, и, как он прокомментировал один из других ответов, лучший подход - хранить ссылку на идентификацию и использовать ворон, включающий - это тот же результат и все еще использует только один запрос.
Другие советы
Я согласен с человеком там, но если вы не можете расширить свою модель, вы всегда можете определить отображение с последующей картой, как это:
Mapper.CreateMap<Foo, FooViewModel>()
.AfterMap(MapAlphaBetaFromFooToFooViewModel);
public void MapAlphaBetaFromFooToFooViewModel(Foo foo, FooViewModel fooViewModel)
{
// Here the code for loading and mapping you objects
}
Таким образом, когда вы сделаете отображение, Automapper автоматически запустит метод после того, как базовое отображение будет выполнено.
Одна оптимизация, которую вы могли бы сделать, - это загрузить связанный документ одну, перейти из Ravendb и не сделать отдельный вызов для загрузки каждого связанного документа. Смотрите документацию Ravendb здесь.
Я не могу думать о каком -либо другом способе отображать эти сущности, кроме как делать это в самом контроллере, как вы показали.
Вы должны заботиться о своей архитектуре на первом месте. Загрузка данных из DB является обязанностью уровня доступа к данным, и она не должна знать, как организован ваш пользовательский интерфейс.
Я думаю, лучший подход - это следующее распределение обязанностей:
- Уровень доступа к данным имеет модель на основе POCO, в которой объекты имеют ссылки друг на друга вместо идентификаторов. Эта модель подвергается воздействию более высоких слоев (например, ViewModel).
- DAL имеет единый метод, который загружает все данные, которые необходимы для предполагаемого действия. В вашем случае он должен загружать как Foo, Alpha, бета -сущности. Поскольку это происходит в одном методе DAL, он, вероятно, может сделать это в одном запросе DB. Хотя главный трюк заключается в том, что другим слоям все равно, как это работает.
- В вашем контроллере вы используете Automapper, чтобы выравнивать объект Foomodel в FooviewModel, которая является одной из функций Authopper.
Ключевые моменты:
- Только Дал знает, как загружает данные. При необходимости вы изменяете его по своему усмотрению без необходимости изменения кода, связанного с автоматом.
- Automapper только отображает данные из одной структуры в другую и не влияет на стратегию загрузки данных.
Тем не менее, я бы только изменил вашу Foomodel:
public class FooModel
{
public int Id { get; set; }
public string Name { get; set; }
public Alpha Alpha { get; set; }
public Beta Beta { get; set; }
}
и код, который загружает его из DB.
Тем не менее, вам может потребоваться ваша оригинальная структура, похожая на Foomodel, чтобы, например, определить картирование объекта с этой простой структурой. Но все же это исключительно для дала.