【问题标题】:Converting from using entity model to using viewmodel从使用实体模型转换为使用视图模型
【发布时间】:2023-03-27 00:25:02
【问题描述】:

当我使用这个查询和实体模型时,一切正常:

var categories = _context.ProductCategories.Include(e => e.Children).ToList();
var topLevelCategories = categories.Where(e => e.ParentId == null);
return View(topLevelCategories);

实体模型:

public class ProductCategory
{
    public int Id { get; set; }
    public int SortOrder { get; set; }
    public string Title { get; set; }

    [ForeignKey(nameof(ParentCategory))]
    public int? ParentId { get; set; }

    public ProductCategory ParentCategory { get; set; } //nav.prop to parent
    public ICollection<ProductCategory> Children { get; set; } //nav. prop to children

    public List<ProductInCategory> ProductInCategory { get; set; }
}

...但是当我尝试改用视图模型时,我收到以下错误消息:

“InvalidOperationException:传递到 ViewDataDictionary 的模型项的类型为 'System.Collections.Generic.HashSet1[MyStore.Models.ProductCategory]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable1[MyStore.Models.ViewModels.ViewModelProductCategory]'。”

我知道发送到视图的模型不正确,但我不明白为什么。

我的视图模型查询:

var VMCategories = _context.ProductCategories
                .Include(e => e.Children).ToList()
                .OrderBy(s => s.SortOrder)
                .Where(r => r.ParentId == null)
                    .Select(v => new ViewModelProductCategory
                    {
                        Id = v.Id,
                        Children = v.Children,
                        ParentId = v.ParentId,
                        Title = v.Title,
                        SortOrder = v.SortOrder
                    })
                    .ToList();
return View(VMCategories);

视图模型:

public class ViewModelProductCategory
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Title { get; set; }
    public int SortOrder { get; set; }

    public string ProductCountInfo =>
        Products != null && Products.Any() ? Products.Count().ToString() : "0";

    public ProductCategory ParentCategory { get; set; } // Nav.prop. to parent
    public IEnumerable<ProductCategory> Children { get; set; } // Nav.prop. to children

    public List<ViewModelProduct> Products { get; set; } // Products in this category
    public List<ViewModelProduct> OrphanProducts { get; set; } // Products with no references in ProductInCategory

}

索引视图:

@model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>

<ul>
    @Html.Partial("_CategoryRecursive", Model)
</ul>

_CategoryRecursive.cshtml:

@model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>

<ul style="list-style:none;padding-left:0px;">
   @if (Model != null)
    {
        foreach (var item in Model)
        {
            if (item.Children != null)
            {
                <li>
                    <ul>
                        @Html.Partial("_CategoryRecursive.cshtml", item.Children)
                    </ul>
                </li>
            }
        }
    }
</ul>

我的错误在哪里?

【问题讨论】:

  • “我无法让它工作:”不是问题陈述。你到底遇到了什么问题?
  • 研究异常,添加.ToList()
  • @CamiloTerevinto 请查看更新。
  • 视图顶部声明的模型类型是什么?
  • ViewModelProductCategory 类的子属性仍然是 public IEnumerable&lt;ProductCategory&gt; 类型,这就是为什么在渲染局部视图 _CategoryRecursive.cshtml 本身时会出错的原因。您需要将 Children 属性更改为 IEnumerable&lt;ViewModelProductCategory&gt; 并正确填充它。

标签: c# linq entity-framework-core


【解决方案1】:

您的视图模型是实体模型和视图模型的混合搭配。例如,视图模型 Children 集合是实体模型类型,而它也应该是视图模型的集合。保持干净,不要让实体模型泄漏到视图模型中

public class ProductCategoryVM
{
    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string Title { get; set; }
    public int? Products { get; set; }
    public IEnumerable<ProductCategoryVM> Children { get; set; }
}

一旦你准备好了

  • 获取所有类别实体模型
  • 也计算产品
  • 将它们映射到类别视图模型

(知道有多个级别的类别)

var query = from category in context.Categories
            let products = from product in context.Products
                            where product.CategoryId == category.Id
                            select product
            select new ProductCategoryVM
            {
                Id = category.Id,
                ParentId = category.ParentId,
                Title = category.Title,
                Products = products.Count(),
                ...
            };
var viewmodels = query.ToList();

下一步可能是从根开始递归地将子级附加到相应的父级。 (查看gist查看全图)

var root = viewmodels
    .Where(p => p.ParentId == null)
    .FirstOrDefault();
root.Traverse(viewmodels);

public static class Extensions
{
    public static void Traverse(this ProductCategoryVM node, IEnumerable<ProductCategoryVM> nodes)
    {
        var children = nodes.Where(p => p.ParentId == node.Id);
        foreach (var child in children)
        {
            child.Traverse(nodes);
        }

        node.Children = children;
    }
}

【讨论】:

  • 1.当孩子也有孩子时,您的查询是否有效? 2. 我的视图模型中的 ProductCountInfo 属性怎么样?我需要每个类别的产品数量。如果我不能(或不应该)在视图模型中引用实体模型,我怎么能得到那个数字?
  • 1.该更新强调了通过对数据库的一次查询检索类别并使用递归对子项进行分组来支持尽可能多的级别的方法
  • 2.使用与结果相关的任何字段装饰您的视图模型,只要确保您没有在其中引用实体模型。对于产品计数,只需在查询时计算数量并将其映射到视图模型中的计数器/产品字段
猜你喜欢
  • 2018-07-10
  • 1970-01-01
  • 2014-11-27
  • 2014-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 1970-01-01
相关资源
最近更新 更多