【问题标题】:Merged parent/child data into a page containing massive viewmodel containing dozens of models - how?将父/子数据合并到包含大量视图模型的页面中,该视图模型包含数十个模型 - 如何?
【发布时间】:2016-03-08 01:10:27
【问题描述】:

因此,我正在开展一个项目,该项目包含一个集中的通信页面,其中包含来自整个站点的所有“注释”。现在我可以在每个模型的基础上带入笔记,但我被三个组困住了,它们不仅需要笔记,还需要按父级划分,以便将每组笔记分配给适当的父母。

作为一个快速细分:

最高层是公司。公司内部的几个部分可以有自己的笔记(多个笔记,所以每个部分都有单独的笔记表),因此从这个级别引入了几个笔记表。

下一个层次是公司可以经历的“循环”。只有最近的周期才会得到处理,所以这最终变得相当简单。此级别包含带有单独注释表的大多数部分,但是通过将当前周期包含在声明中,我可以仅过滤当前周期。

最低级别涉及一些挂起循环的表。由于这些不像循环那样受到限制,因此我不仅需要能够引入这些表格,还需要能够引入这些表格中每个条目的注释。虽然可以通过 Cycle 声明过滤表格,但 notes 表格没有这些信息。

我的问题在于这个最低级别。我不知道如何创建一个“多 IEnumerable”,以便父数据和子数据都可以附加到单个 viewModel 容器。这使我无法创建可以填充该容器的 Linq Lambda 表达式。一旦我装满了容器,剩下的就应该很容易了——我只需要创建一对嵌套的 for 循环,它们首先提取父级(只是表格行的名称)和子级(来自注释的所有注释条目该表行的表)。

换句话说,我需要一个左连接来填充单个视图模型,并且我无法做到这一点,直到我知道如何创建一个包含两个模型的 IEnumerable。

Parent
+----+------+
| id | name |
+----+------+
|  1 | John |
|  2 | Mike |
-------------

Child
+----+-----+-----------------+------------+
| id | Pid | note            | date       |
+----+-----+-----------------+------------+
|  1 |   2 | Mike's notes    | 2016-01-15 |
|  2 |   1 | John’s notes    | 2016-02-22 |
|  3 |   2 | More Mike’s     | 2016-02-24 |
|  4 |   2 | Yet more Mike’s | 2016-03-01 |
-------------------------------------------

Page output

            John
2016-02-22 John’s Notes
[text field] [submit]

            Mike
2016-01-15 Mike’s notes
2016-02-24 More Mike’s
2016-03-01 Yet more Mike’s
[text field] [submit]

另外,请记住,这只是被引入页面的众多模型之一,现在我在同一页面上有将近 20 个,所以我的控制器以 var viewModel = new CommunicationsViewModel(); 开头并引入了多个模型通过控制器模型中的 IEnumerables 将它们附加到viewModel。我唯一的问题是我需要将两个模型附加到一个 IEnumerable,以便我的 JOIN 查询可以成功地将内容转储到 viewModel 容器中。

现在,我最有希望的 Lambda 表达式是:

viewModel.Active = await db.Activity
  .Join(
    db.ActivityNotes.Include(y => y.NotesStatus),
    act => act.ActivityId,
    actnot => actnot.ActivityId,
    (act, actnot) => new { Activity = act, ActivityNotes = actnot }
  )
  .Where(act => act.CycleId == cycleId)
  .ToListAsync();

当然,在我弄清楚如何将 Activity 和 ActivityNotes 组合成一个名为 Active 的单个 IEnumerable 之前,我无法对它做任何事情。

编辑:致埃里克·飞利浦:

对于我的通讯页面,我制定了以下模型:

public class CommunicationsViewModel {
  ...more IEnumerated models...
  public IEnumerable<Activity> Active { get; set; }
  public IEnumerable<ActivityNotes> ActiveComm { get; set; }
}

注意最后两个条目,父项和子项。父节点需要为循环 Guid 过滤,仅此而已。

我的 OnModelCreating:

#region Activity
modelBuilder.Entity<Activity>().HasMany(x => x.ActivityNotes).WithRequired(x => x.Activity).HasForeignKey(x => x.ActivityId).WillCascadeOnDelete(true);
modelBuilder.Entity<Activity>().HasKey(x => x.ActivityId);
modelBuilder.Entity<Activity>().Property(x => x.ActivityId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Activity>().Property(x => x.CycleId).IsRequired();
modelBuilder.Entity<Activity>().Property(x => x.ActivityName).HasColumnType("nvarchar").HasMaxLength(50).IsOptional();
modelBuilder.Entity<Activity>().Property(x => x.ActivityDate).HasColumnType("date").IsRequired();
... redundant content removed ...
#endregion
#region ActivityNotes
modelBuilder.Entity<ActivityNotes>().HasKey(x => x.ActivityNotesId);
modelBuilder.Entity<ActivityNotes>().Property(x => x.ActivityNotesId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ChampionNotes>().Property(x => x.Notes).IsRequired();
#endregion

我的索引,高度删节,仅包含相关部分:

public async Task<ActionResult> Index() {
  var cycleId = new Guid(User.GetClaimValue("CWD-Cycle"));
  var viewModel = new CommunicationsViewModel();
  viewModel.Active = await db.Activity.Where(x => x.CycleId == cycleId).Where(x => x.Active == true).ToListAsync();
  ...more viewModelled content brought in...
  return View(viewModel);
}

还有我观点的内容:

@foreach(var item in Model.Active) {
  @Html.DisplayFor(modelItem => item.ActivityName)
  foreach(var subItem in Model.ActiveComm) {
    @Html.DisplayFor(modelItem => subItem.Notes)
  }
}

…啊,它在第二个foreach 上基本上失败了,经典的“对象引用未设置为对象的实例”。这对于 ASP.NET 来说是个通俗易懂的说法:“哎呀,你真搞砸了;祝你好运找到哪里。”

【问题讨论】:

  • 如果你告诉 EF 有一个 foreign key and setup navigation properies...
  • 我非常小心地在 EF 6 和 Fluent Validation 中包含外键引用。这意味着我的代码优先数据库具有完整的关系,所有关系都根据需要具有适当的级联/忽略设置。所以你的意思是我所要做的就是引入我的活动,然后执行双重for 循环以吐出按活动分组的笔记?不知何故,这听起来不对;设置中应该涉及更多工作,以便注释可以明确地定位在其for 循环中。

标签: asp.net-mvc linq asp.net-mvc-5 entity-framework-6


【解决方案1】:

我不知道如何创建“多 IEnumerable”,以便父数据和子数据都可以附加到单个 viewModel 容器。

我完全不明白这句话……。这是一个包含父模型和子模型的 ViewModel:

public class MyModel
{
  public IEnumerable<Parent> Parents { get; set; }
  public IEnumerable<Child> Children { get; set; }
}

虽然像下面这样的东西看起来确实是你想要的,而且仍然很简单。

public class MyModel
{
  public IEnumerable<Parent> Parents { get; set; }
}

public class Parent
{
  public int Id { get; set; }
  public string Name { get; set; }
  public ICollection<Child> Children { get; set; }
}

public class Child
{
  public int Id { get; set; }
  public int Pid { get; set; }
  public Parent Parent { get; set; }
  public string Note { get; set; }
  public DateTime Date { get; set; }
}

public class MyDbContext : DbContext
{
  public override void OnModelCreating(DbModelBuilder mb)
  {
    mb.Entity<Parent>()
      .HasKey(p => p.Id);

    mb.Entity<Child>()
      .HasKey(c => c.Id)
      .HasRequired(c => c.Parent)
      .WithMany(p => p.Children)
      .HasForeignKey(c => c.Pid);
  }
}

public ActionResult SomeMethod()
{
  var viewModel = new MyModel
  {
    Parents = db.Set<Parent>()
      .Where(p => p.Id == 1)
      .Include(p => p.Children)
      .AsNoTracking()
      .ToList();
  };

  return View(viewModel);
}

对象引用未设置为对象的实例”。这对于 ASP.NET 来说是个通俗易懂的说法:“哎呀,你真搞砸了;祝你好运找到哪里。”

不,它只意味着一件事,它总是意味着同样的事情。这意味着您要求运行时引擎访问一个从未被赋值的变量 (most of the time it's the programmers error)。

你有这个模型:

public class CommunicationsViewModel {
  public IEnumerable<Activity> Active { get; set; }
  public IEnumerable<ActivityNotes> ActiveComm { get; set; }
}

你给出的唯一代码是:

viewModel.Active = await db.Activity
  .Where(x => x.CycleId == cycleId)
  .Where(x => x.Active == true)
  .ToListAsync();

所以非常很明显你从来没有设置过:

Model.ActiveComm

这是我对这篇文章的最后一次编辑。您缺少基本的 c# 理解和基本的调试技能。我建议您花一些时间查看http://www.asp.net/web-pages/overview/testing-and-debugging/introduction-to-debugginghttp://www.asp.net 上提供的其他资源。祝你好运!

【讨论】:

  • 好的,我不想实际设置任何父级,我想带回一个包含所有父级(当前循环的)和每个父级的所有关联子级的数组。按照我的原话,我需要过滤仅属于当前循环一部分的父母,并且我需要带回所有过滤父母的孩子。
  • 另外,我需要让孩子直接与其关联的父母分组,而不是页面上的其他地方。
  • 我需要过滤只属于当前周期一部分的父母,所以更新where() 语句,这不会那么难... 我需要带回所有被过滤的父母的孩子它已经用Include()做到了。
  • 我已经更新了我原来的帖子。它与视图失败,第二个 foreach 应该在第一个 foreach 中引入与父级关联的子级。
【解决方案2】:

我刚刚发现,关键在于如何将两个 foreach 循环链接在一起,以及如何引入特定的数据块。

首先,只查看父级,但包括子级:

viewModel.Parent = await db.Parent
  .Where(x => x.ForeignKey == limitingFactor)
  .Include(x => x.Child)
  .ToListAsync();

要点:只有父级必须以IEnumerable 的形式出现在页面的 ViewModel 中,这就是我被可怕地挂断的原因——我认为两者都必须被引入。他们没有。由于我们只是明确地将父级引入viewModel,如上所示,任何子级内容都会通过.Include() 进入父级的尾随(以旋转隐喻)。它们根本不需要在页面的 ViewModel 中作为 IEnumerable 引用。

这与 Erik Philips 在上面的帖子中所说的非常吻合,完全归功于 Erik 在ActionResult 中指出这一点。然而,在这一点上,我仍然不知道如何在每个父母的基础上“抓取”孩子的数据……据我所知,它并没有被带进来。然而,当我胡思乱想时,我绊倒了正确的方法。

纯属偶然,我在第一个循环的基础上构建了第二个 foreach 循环,并且成功了。

第一个,外面是给父母的:

@foreach(var parent in Model.Parent){
  @Html.DisplayFor(modelItem => parent.RandomFieldNameInParent)

因为每个父母都需要有一个孩子的列表:

foreach(var child in parent.Child){

注意子表是如何被引入的?我们已经过滤了父项,此时我们只查看一个父项,在这个内部foreach 循环中,我们正在查找与该父项相关的子表中的所有子项。这就是为什么它是parent.Child,而不是Model.Child。它是来自外部foreach 循环的与父级相关的子级内容(任意数量的子级)。

生成的代码如下所示:

@foreach(var parent in Model.Parent){
  @Html.DisplayFor(modelItem => parent.RandomFieldNameInParent)
  foreach(var child in parent.Child){
    @Html.DisplayFor(modelItem => child.RandomFieldNameInChild)
  }
}

这正是我从一开始就一直在寻找的最终结果。原谅我,我去给自己倒一个僵硬的;答案的简单性与寻找答案所付出的努力完全不成比例。

【讨论】:

    猜你喜欢
    • 2011-11-07
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-07
    • 2016-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多