从使用实体模型转换为使用viewmodel

时间:2018-01-11 12:12:00

标签: c# linq entity-framework-core

当我使用这个查询和实体模型时,事情正在发挥作用:

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

...但是当我尝试切换到使用viewmodel时,我收到此错误消息:

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

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

我的viewmodel查询:

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

viewmodel:

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>

我/我的错误在哪里?

1 个答案:

答案 0 :(得分: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();

下一步可能是以root身份递归地将子项附加到相应的父项。 (查看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;
    }
}