【问题标题】:LINQ query to find related data [duplicate]LINQ查询以查找相关数据[重复]
【发布时间】:2018-01-03 13:01:10
【问题描述】:

我之前在 StackOverflow 上的递归产品类别树视图方面得到了一些帮助,这很有效:

实体模型:

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

控制器:

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

查看:

@if (Model != null)
{
    foreach (var item in Model)
    {
    <li>
        @item.Title
        <ul>
            @Html.Partial("_CategoryRecursive.cshtml", item.Children)
        </ul>
    </li>
    }
}

但是当我尝试将此设置转换为我的视图模型时(并添加一个用于计算每个类别中的产品的属性,以及一个没有任何类别连接的产品列表)...:

视图模型:

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 ViewModelProductCategory ParentCategory { get; set; } // Nav.prop. to parent
    public IEnumerable<ViewModelProductCategory> 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 reference in ProductInCategory
}

控制器:

            var VMCategories = _context.ProductCategories
                        .Include(e => e.Children)
                        .OrderBy(s => s.SortOrder)
                        .Where(r => r.ParentId == null) // only need root level categories in the View
                        .Select(v => new ViewModelProductCategory
                        {
                            Id = v.Id,
                            ParentId = v.ParentId,
                            Title = v.Title,
                            SortOrder = v.SortOrder,
                            // get products without a 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();
        return View(VMCategories);

查看:

@if (Model != null)
{
    foreach (var item in Model)
    {
    <li>
        @item.Title (@item.ProductCountInfo)
        <ul>
            @Html.Partial("_CategoryRecursive.cshtml", item.Children)
        </ul>
    </li>
    }
}

...它不再起作用了。视图确实呈现,但它只显示根类别。看来我修改后的查询不会获得任何子类别。当我检查查询结果时,Children 属性为空。

编辑

我将使用@Rainman 的解决方案,并将我的查询.Select 更改为包含Children = v.Children,,并因此更改我的视图模型导航属性:

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

我还创建了新的视图模型 CategoryRecursiveModel 并将我的视图更改为:

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

<ul>
@if (Model != null)
{
    foreach (var item in Model)
    {
        <li>
            @item.Title (@item.ProductCountInfo)
            <ul>
                @Html.Partial("_CategoryRecursive.cshtml", new CategoryRecursiveModel
                {
                    Children = item.Children.ToList();
                })
            </ul>
        </li>
    }
}
</ul>

现在我遇到了 InvalidOperationException,因为视图需要 ViewModelProductCategory 的 IEnumerable,但接收到 CategoryRecursiveModel。

【问题讨论】:

  • 您使用的是 Entity Framework Core 还是 Entity Framework 6?
  • 实体框架核心
  • 您似乎错过了这样一个事实,即ToList 在这里调用var categories = _context.ProductCategories.Include(e =&gt; e.Children).ToList(); 对您的原始解决方案至关重要,因为它迫使 EF 将整个树加载到内存中并完成繁重的连接工作导航属性(所谓的导航属性修复)。一旦你开始使用投影,你必须自己完成所有 Parent / Children 属性填充。
  • 虽然标记为重复的问题是针对 EF6 的,但同样的原理和解决方案也适用于 EF Core。

标签: c# linq entity-framework-core


【解决方案1】:

因为您没有选择Children 进行第二次查询;

var VMCategories = _context.ProductCategories
    .Include(e => e.Children)
    .OrderBy(s => s.SortOrder)
    .Where(r => r.ParentId == null) // only need root level categories in the View
    .Select(v => new ViewModelProductCategory
    {
        Id = v.Id,
        Children = v.Children, // Select it
        ParentId = v.ParentId,
        Title = v.Title,
        SortOrder = v.SortOrder,
        // get products without a 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 ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } //nav. prop to children

此外,导航属性仅适用于 EF 实体,而不是 ViewModelProductCategory 模型类或其他类。

编辑

_CategoryRecursive视图创建模型类;

public class CategoryRecursiveModel
{
    public List<ProductCategory> Children { get; set; }
}

并改变主视图;

@if (Model != null)
{
    foreach (var item in Model)
    {
        <li>
            @item.Title (@item.ProductCountInfo)
            <ul>
                @Html.Partial("_CategoryRecursive.cshtml", new CategoryRecursiveModel
                {
                    Children = item.Children.ToList();
                })
            </ul>
        </li>
    }
}

【讨论】:

  • 有什么方法可以在视图中使用我的视图模型吗?在更改后使用视图模型时,我收到 InvalidOperationException,因为视图需要我的视图模型,并且您的更改使模型成为 System.Collections.Generic.HashSet`1[MyStore.Models.ProductCategory]。如果我将视图更改为期望实体模型,我的 ProductCountInfo 和 OrphanProducts 属性将丢失。在视图中使用实体模型时是否存在一些安全问题?
  • 我更新了答案。
  • 我收到 InvalidOperationException,因为视图需要我的视图模型,但接收到 CategoryRecursiveModel。
猜你喜欢
  • 1970-01-01
  • 2013-05-07
  • 2014-08-31
  • 2012-09-28
  • 1970-01-01
  • 2013-02-03
  • 2020-10-31
  • 1970-01-01
相关资源
最近更新 更多