Создание MVC ViewModel с несколькими запросами с использованием Entity Framework - PullRequest
0 голосов
/ 31 октября 2018

Я довольно новичок в MVC и пытаюсь узнать больше о ViewModels. У меня есть страница категории продуктов, на которой я хотел бы отобразить категории продуктов, а также продукты и связанные с ними изображения. Я собираюсь упростить некоторые из этих таблиц и просто сосредоточиться на логике возврата этих данных в представление. У меня есть представление, работающее с заполненным выпадающим списком, но я не уверен, как заполнить ProductViewModel в CategoryViewModel.

База данных

Таблица категорий

CategoryId
CategoryName
CategoryDescription

Таблица продуктов

ProductId
ProductName
ProductDescription
ProductPrice
CategoryId

Таблица ProductImage

ProductId
ProductImage1
ProductImage2
ProductImage3
ProductImage4
ProductImage5
ProductImage6
ProductImage7
ProductImage8
ProductImage9
ProductImage10

ViewModel

public class ProductViewModel
{
  public Product ProductVM { get; set; }
  public ProductImage ProductImageVM { get; set; }
}

public class CategoryViewModel
{
  public List<Category> Category { get; set; }
  public List<ProductViewModel> Products { set;get;} 
}

Контроллер

public ActionResult Index()
{
    var model = new CategoryViewModel();
    model.Category = db.Categories.OrderBy(d => d.CategoryName).ToList();
    model.Products = from p in db.Products
                             join pi in db.ProductImages on p.ProductId equals pi.ProductId
                             orderby p.ProductPrice descending

    return View(model);
}

View

@model CategoryViewModel

@Html.DropDownListFor(x => x.CategoryId, new SelectList(Model.Category, "CategoryId", "CategoryName"), "View all Categories")

<table>
@foreach (var product in Model.Products)
    {
        <tr>
            <td>@item.ProductImage.ProductImage1</td>
            <td>@item.Product.ProductName</td>
            <td>@item.Product.ProductPrice</td>
            <td>@item.Product.ProductDescription</td>
        </tr>
    }
</table

Ответы [ 2 ]

0 голосов
/ 31 октября 2018

Я использую аналогичный подход к Джеку, за исключением того, что я разделяю его дальше.

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

Я бы создал два класса:

  • ProductViewModelFactory (создает ProductViewModel)
  • CategoryViewModelFactory (создает CategoryVieWModel)

ProductViewModelFactory может внутренне использовать любое количество репозиториев, сервисов или других необходимых фабрик моделей представления для построения своих данных.

В этом случае ProductViewModelFactory будет внутренне вызывать CategoryViewModelFactory для создания CategoryViewModel. ProductViewModelFactory также, вероятно, вызовет ProductRepository или ProductService и отобразит возвращенный Product, извлеченный из какого-либо постоянного хранилища, в модель представления.

Это может вызвать и другие вещи - если этого не произойдет сейчас, велика вероятность, что это произойдет позже, когда будет добавлено больше функций.

CategoryViewModelFactory, вероятно, вызовет CategoryService или CategoryRepository и отобразит эти данные в CategoryViewModel.

Итак, какие преимущества дают нам эти дополнительные слои? В конце концов, это больше работы.

Конечно, есть YAGNI, но, по моему опыту, этот подход дает наибольшую гибкость в обработке неожиданных требований при минимальном объеме работы.

В те времена, когда вы думали о YAGNI, но вы только что узнали, вам действительно понадобится , это может вызвать некоторые катастрофические ситуации - так что, ИМХО, стоит приложить дополнительные 20% усилий, чтобы создать простую последовательная структура, которая в большинстве случаев гарантирует, что вы не рисуете себя в углу.

  1. ViewModelFactories полностью отделены от репозиториев или сервисов, которые фактически получают данные.

Это означает, что когда требуется совершенно иное представление, вы не перебираете свои сервисы или репозиторий - они должны возвращать самые необработанные данные.

Они также могут вызывать другие ViewModelFactories, что становится удобным, так как во многих случаях по мере роста приложения вам нужно включать другие ViewModel в ViewModel внутри ViewModel внутри ... вы получаете точку.

  1. Становится очень просто макет поддельного ViewModelFactory, Repository или Service и внедряет его с помощью внедрения зависимостей, когда вам нужно что-то протестировать или вы не можете использовать реальную реализацию по какой-либо причине.

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

  1. Работа с любым классом в приложении становится простой, поскольку все является черным ящиком.

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

Если вам нужен кусок данных, вы просто внедряете его фабрику, сервис или хранилище модели представления в зависимости от того, что вам нужно, передаете некоторые аргументы, и все работает в предсказуемом, согласованном порядке по всему приложению.

Итак, подведем итог , вот примерный жизненный цикл:

  1. Клиент делает запрос к действию контроллера.
  2. Действие контроллера вызывает фабрику модели единого представления, чтобы получить данные для представления действия.
  3. Фабрика модели представления вызывает любое количество других служб, репозиториев или других фабрик моделей представления (которые, в свою очередь, могут вызывать свои собственные службы, репозитории и фабрики моделей просмотра ...)
  4. Фабрика модели вида возвращает готовую модель вида.
  5. Действие контроллера берет возвращенную модель представления и помещает ее в представление.
  6. Клиент получает отрендеренный HTML.

Это также может дать вам несколько интересных опций для автоматической обработки вещей на основе соглашений об именах с фильтрами действий, что приводит к очень пустым действиям контроллера.

0 голосов
/ 31 октября 2018

Всякий раз, когда я использую ViewModels, я также разрабатываю класс Service для его заполнения. Это помогает содержать мой контроллер в чистоте и изолировать мою логику.

Сначала создайте папку с именем «Services» для хранения этих классов. При использовании Области создайте папку на том же уровне в иерархии проекта, что и Контроллер, использующий ее.

Затем создайте класс «Сервис» в этой папке. Например, я бы создал класс с именем CategoryService, поскольку ViewModel с именем CategoryViewModel.

В этом классе я бы поместил код для инициализации ViewModel:

public class CategoryServices
{
    private MyDbContext db = new MyDbContext();

    internal CategoryViewModel GetCategoryViewModel(){
        return new CategoryViewModel(){
            Category = GetCategories(),
            Products = GetProductViewModel()
        };
    }

    internal List<Category> GetCategories(){
        return db.Categories.OrderBy(d => d.CategoryName).ToList();
    }

    internal List<ProductViewModel> GetProductViewModel(){
        return db.Products.Select(x => new ProductViewModel()
        {
            ProductVM = x,
            ProductImageVM = x.ProductImage
        });
    }
}

Теперь вы можете легко получить ViewModel из вашего контроллера:

public ActionResult Index()
{
    CategoryService service = new CategoryService();
    return View(service.GetCategoryViewModel());
}

По вашему мнению, вам придется обновить ссылки на ваши модели, чтобы обрабатывать то, что находится в ViewModel.

@model CategoryViewModel

@Html.DropDownListFor(x => x.CategoryId, new SelectList(Model.Category, "CategoryId", "CategoryName"), "View all Categories")

<table>
@foreach (var item in Model.ProductViewModels)
    {
        <tr>
            <td>@item.ProductImage.ProductImage1</td>
            <td>@item.Product.ProductName</td>
            <td>@item.Product.ProductPrice</td>
            <td>@item.Product.ProductDescription</td>
        </tr>
    }
</table

Это должно указать вам общее направление, в котором вы должны идти. Не стесняйтесь оставлять комментарии, если у вас есть вопросы по поводу чего-либо выше, и я постараюсь уточнить.

EDIT: Кроме того, я бы порекомендовал разбить функции в вашем классе обслуживания еще дальше. Я избегал этого здесь, потому что я не хотел предоставлять вам класс функций более 10+.

EDIT2: Обновлена ​​функция GetProductViewModel(). Поскольку между моделями Product и ProductImage существует отношение «один к одному», а ProductImage имеет внешний ключ для ProductId, ссылающийся на ProductId продукта, ProductImage должен быть доступен как дочерняя сущность в модели Product.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...