使用实体框架创建具有多个查询的MVC ViewModel

时间:2018-10-31 18:22:55

标签: asp.net asp.net-mvc entity-framework

我对MVC相当陌生,并尝试对ViewModels进行更多探索。我有一个产品类别页面,我想显示产品类别以及产品及其相关图像。我将简化其中一些表,仅关注将这些数据返回到视图的逻辑。我有使用填充下拉列表的视图,但是我不确定如何在CategoryViewModel中填充ProductViewModel。

数据库

类别表

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);
}

查看

@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 :(得分:2)

每当我使用ViewModels时,我也会开发一个Service类来填充它。这有助于保持控制器清洁并隔离逻辑。

首先,创建一个名为“服务”的文件夹以包含这些类。如果使用Areas,则在项目层次结构中与使用Controller的控制器在同一级别创建文件夹。

然后,在该文件夹中创建一个“服务”类。例如,由于ViewModel被命名为CategoryService,因此我将创建一个名为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

这应该将您设置在需要走的大致方向上。如果您对以上任何问题有任何评论,请随时离开。我将尽力澄清。

编辑: 另外,我建议您进一步细分服务类中的功能。我在这里避免了这种情况,因为我不想为您提供10个以上的函数类。

EDIT2: 更新了GetProductViewModel()函数。由于Product和ProductImage模型之间存在1对1的关系,并且ProductImage在ProductId上具有引用产品的ProductId的外键,因此ProductImage应该可以作为Product模型上的子实体使用。

因此,您可以使用方便的lambda表达式在一个数据库行程中生成ProductViewModels的列表。我已经使用此lambda表达式生成了很多列表,但是您可能需要对其进行修改才能正常工作。

答案 1 :(得分:1)

我与杰克采取了类似的方法,只是我将其进一步分离了。

似乎有些矫kill过正(在某些情况下可能如此),但它为您提供了更大的灵活性。

我将创建两个类:

  • ProductViewModelFactory(创建ProductViewModel
  • CategoryViewModelFactory(创建CategoryVieWModel

ProductViewModelFactory可以在内部使用任何数量的存储库,服务或其他必要的视图模型工厂来构建其数据。

在这种情况下,ProductViewModelFactory将在内部调用CategoryViewModelFactory以创建CategoryViewModelProductViewModelFactory也可能会调用ProductRepositoryProductService并将从某种持久性存储中检索到的返回的Product映射到视图模型。

它可能还会调用其他一些内容-如果现在还不行,那么以后添加更多功能的机会就很大。

CategoryViewModelFactory可能会调用CategoryServiceCategoryRepository,然后将数据映射到CategoryViewModel

那么,这些额外的层给我们带来了什么优势?毕竟,这需要更多的工作。

当然,有YAGNI,但是根据我的经验,这种方法以最少的工作量提供了最大的灵活性来处理意外的要求。

您认为YAGNI的时代,但是您刚刚发现您确实需要 ,这可能会导致一些灾难性的情况-因此恕我直言,值得付出额外20%的精力来创建一个简单一致的结构,在大多数情况下,可以确保您不会陷入困境。

  1. ViewModelFactories与实际检索数据的存储库或服务完全分开。

这意味着当需要一个完全不同的视图时,您不必最终弄乱您的服务或存储库-这些应该返回可能的最原始数据。

他们还可以调用其他ViewModelFactories,这很方便,因为随着应用程序的增长,很多时候您需要在ViewModel内的ViewModel中包括其他ViewModels。

  1. 当您需要测试某些东西,或者由于某种原因而无法使用实际实现时,模拟假的ViewModelFactory,存储库或服务并通过依赖注入将其注入变得非常简单。

这种情况比您在以下情况下想的要多  -在某些情况下需要测试的错误,  -另一个相关组件未完成,  -您需要返回一组特定的数据才能进行前端开发的视图,  -您要创建一系列测试,  -您需要为客户端演示等返回特定数据

  1. 由于所有内容都是黑匣子,因此在应用程序中使用任何类都变得非常简单。

您不必关心视图模型工厂,服务或存储库如何获取或返回数据。您不必担心当前正在上课的人在消费什么。

如果需要大量数据,只需根据需要注入其视图模型工厂,服务或存储库,并传递一些参数,然后一切在整个应用程序中以可预测的一致模式工作。

因此,总结,以下是大概的生命周期:

  1. 客户端向控制器请求动作。
  2. 控制器动作调用单个视图模型工厂以获取动作视图的数据。
  3. 视图模型工厂调用任意数量的其他服务,存储库或其他视图模型工厂(它们可以依次调用其自己的服务,存储库和视图模型工厂...)
  4. 视图模型工厂返回完成的视图模型。
  5. 控制器动作采用返回的视图模型并将其推入视图。
  6. 客户端获取呈现的HTML。

这还可以为您提供一些有趣的选项,以基于带有动作过滤器的命名约定自动处理事物,从而导致非常空的控制器动作。