Linq查询以递归方式从树中读取

时间:2018-01-10 10:10:57

标签: c# linq entity-framework-core

我的查询应该从数据库中检索产品类别信息并填充viewmodel ViewModelProductCategory。这些类别将以递归方式排列在树形结构中,每个类别都有一个产品列表。此外,还有一个没有任何类别参考的产品列表,即所谓的孤儿产品。

当查询到达.Select(v => ...时,出现问题,并且" ArgumentNullException:Value不能为null"扔了:

var VMCategories = _context.ProductCategories
                        .Include(e => e.Children).ToList()
                        .OrderBy(s => s.SortOrder)
                        .Where(r => r.ParentId == null) // only need root level categories
                        .Select(v => new ViewModelProductCategory
                        {
                            Id = v.Id,
                            Children = v.Children,
                            ParentId = v.ParentId,
                            Title = v.Title,
                            SortOrder = v.SortOrder,
                            // Get all orphan products (products without category):
                            OrphanProducts = v.ProductInCategory
                                            .Where(o => !_context.ProductsInCategories.Any(pc => o.Id == pc.ProductId))
                                            .Select(orph => new ViewModelProduct
                                            {
                                                Id = orph.Product.Id,
                                                Title = orph.Product.Title,
                                                Price = orph.Product.Price,
                                                Info = orph.Product.Info,
                                                SortOrder = orph.SortOrder
                                            })
                                            .OrderBy(s => s.SortOrder)
                                            .ToList()

                        })
                        .ToList();

以下是我的实体和视图模型:

产品类别的实体模型:

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

产品的实体模型:

public class Product
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Info { get; set; }
    public decimal Price { get; set; }
    public List<ProductInCategory> InCategories { get; set; } // Nav.prop. to occurances in product categories
}

catgegory中产品的实体模型:

public class ProductInCategory
{
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int SortOrder { get; set; }
    public int ProductCategoryId { get; set; }
    public Product Product { get; set; } // Nav.prop. to product
    public ProductCategory ProductCategory { get; set; } // Nav.prop. to category
}

产品类别的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
    {
        get
        {
            return 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
}

具有类别标题的产品类别的Viewmodel(用于在产品视图中显示类别标题):

public class ViewModelCategoryWithTitle
{
    public int Id { get; set; }
    public string Title { get; set; }
}

产品的Viewmodel:

public class ViewModelProduct
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Info { get; set; }
    public decimal Price { get; set; }
    public int SortOrder { get; set; }
    public IEnumerable<ViewModelCategoryWithTitle> Categories { get; set; }
}

附录

我忘了添加视图。首先,将模型发送到Index-view,如下所示:

@model IEnumerable<MyStore.Models.ViewModels.ViewModelProductCategory>
<ul>
    @Html.Partial("_CategoryRecursive", Model)
</ul>

然后Index-view调用这个递归的razor-view:

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

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

1 个答案:

答案 0 :(得分:0)

我认为评论者对于分解它是正确的。显然我没有你的数据就无法测试它,但我最初会尝试这样的事情:

var cpics = _context.ProductsInCategories;

var vmc = from pc in _context.ProductCategories
          where pc.ParentId == null
          orderby pc.SortOrder
          select new ViewModelProductCategory(pc, cpics);

public class ViewModelProduct
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Info { get; set; }
    public decimal Price { get; set; }
    public int SortOrder { get; set; }
    public IEnumerable<ViewModelCategoryWithTitle> Categories { get; set; }

    public ViewModelProduct(ProductInCategory pic)
    {
        Id = pic.Product.Id;
        Title = pic.Product.Title;
        Price = pic.Product.Price;
        Info = pic.Product.Info;
        SortOrder = pic.SortOrder;
    }

    public ViewModelProduct() { }
}

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

    public ViewModelProductCategory(ProductCategory pc, IEnumerable<ProductInCategory> cpics)
    {
        Id = pc.Id;
        Children = pc.Children;
        ParentId = pc.ParentId;
        Title = pc.Title;
        SortOrder = pc.SortOrder;
        OrphanProducts = (from pcpic in pc.ProductInCategory
                          join cpic in cpics
                              on pcpic.Id equals cpic.ProductId
                              into pics
                          from pic in pics.DefaultIfEmpty()
                          where pic == null
                          select new ViewModelProduct(pcpic))
                         .OrderBy(s => s.SortOrder)
                         .ToList();
    }

    public ViewModelProductCategory() { }
}

检查空item.Children

@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>

可能有太多的@ - 我不太使用MVC。