【问题标题】:How to retrieve a list of record including only one child each?如何检索每个仅包含一个孩子的记录列表?
【发布时间】:2019-05-20 06:59:13
【问题描述】:

示例模型。

public class Root
{
    public string Id { get; private set; }
    public ICollection<Child> Children { get; private set; }
}

public class Child
{
    public string Id { get; private set; }
    public string RootId { get; private set; }
    public string Code { get; private set; }
    public string Name { get; private set; }
}

约束。

Child 具有 RootIdCode 属性作为其唯一键。这意味着每个 Root 对象只允许有尽可能多的 Child 对象,只要没有两个或更多 Child 包含相同的代码。

示例查询

获取所有具有 Child 且 Code 等于 A100 的 Root 记录。

包含两个根对象的示例列表数据

Root1 with 2 children, one having a code A100 and the other A200.

Root2 with 2 children, one having a code A100 and the other A500.

我现在正在做的当前查询是首先获取所有根记录及其所有子记录。然后,迭代每条记录并删除其所有与我查询的代码不同的子项。这种方法的问题是当数据库增长时,它会对这种方法产生影响,因为当我只需要每个 Root 对象一个时,我会检索所有子对象。

示例代码

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .ToList();

foreach (var root in records)
{
    foreach (var child in root.Children)
    {
        if (!child.Code == "A100")
        {
            root.Children.Remove(child);
        }
    }
}

我的模型按照 DDD 原则将其属性设置器设置为私有。所以我不能使用 Select() 命令进行 linq 投影,如下所示。

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .Select(x => new Root{...})
    .ToList();

在我的情况下使用构造函数也不理想,因为作为每个模型设计的一部分,我在实例化期间将每个对象的状态设置为 Created

编辑 1

我可以使用 Select() 在 LINQ 投影中使用构造函数,但我的问题是,在我的所有模型中,都有一个名为 State 的属性,我在模型中的各个点进行更新,具体取决于关于发生的事情。在构造函数部分,我将其更新为 Create 状态,以暗示新模型已创建。因此,如果我要创建一个构造函数,以便从数据库中创建模型的实例,那会导致混乱,因为我只是从数据库中检索一个已经存在的记录,并且如果我要使用构造函数,代码在实例化过程中会将模型标记为已创建,这不是我想要的,因为它会在我的设计中创造新的意义。

编辑 2

我很抱歉没有让自己足够清楚。我的问题在于查询的这一部分。

第 1 部分。

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .ToList();

所以我不需要到达这部分。

第 2 部分

foreach (var root in records)
{
    foreach (var child in root.Children)
    {
        if (!child.Code == "A100")
        {
            root.Children.Remove(child);
        }
    }
}

现在基于我提到的约束。

约束 1. 不使用公共设置器,所以我不能使用它。

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .Select(x => new Root{...})
    .ToList();

约束 2. 不使用构造函数

var records = context.Roots
    .Include(x => x.Children)
    .Where(x => x.Children.Any(y => y.Code == "A100"))
    .Select(x => new Root(...))
    .ToList();

最重要的是,是否有我可以使用的查询或任何其他方法直接从数据库中获取我想要的记录,而无需执行查询的第二部分?

【问题讨论】:

  • 澄清一下:您正在使用示例代码删除所有非 A100 节点从原始列表数据。如果这不是您的意图,而您实际上想要一个副本,那么您需要一种方法来构造或复制至少您的根项目,这会使解决方案变得更加容易。
  • 是的,我正在从从数据库检索到的列表中删除所有非 A100 节点,因为使用此命令获取所有子记录 Where(x => x.Children.Any(y => y.代码 == “A100”))。我试图弄清楚如何只获取一个子记录,而不是根据我上面提到的约束来检索所有子记录。
  • 如果在select 子句中将查询结果展平为匿名对象会怎样?
  • 您对实体模型施加了太多约束,这应该是logical data model。一般来说,将domain model 与 LDM 混合不是一个好主意 - LDM 不能具有所有这些约束(私有设置器、get/set 行为、构造函数逻辑等)。更好地创建单独的模型并在需要时在两者之间进行映射。无论如何,您使用的是什么 EF(EF6、EF Core(版本?))?根据这一点,这个特定问题可能会有解决方案。
  • @devpro101,我在下面添加了我的答案,试试看,让我知道:)

标签: c# entity-framework linq domain-driven-design


【解决方案1】:

尝试传统的 LINQ,这样您就不再需要手动删除子对象并将查询结果投影到匿名对象。

var result = (from root in context.Roots.Include(x => x.Children)
          from child in root.Children
          where child.Code == "A100"
          select new
          {
              Id = root.Id,
              Children = child
          }).ToList();

【讨论】:

  • 不错的一个。值得一提的是,这仍然会检查根项的所有子项。
  • @kaedinger,是的,可以在集合上应用特定的过滤器,如 A100,它需要使用 where 子句进行检查,如果存在则返回
  • @er-sho,当然,除非您手动检查(请参阅我的解决方案)或使用FirstOrDefault,您需要在其中应用额外的过滤。此外,您的解决方案中的Children 不是集合。但是由于 OP 多次更改了他的场所,我想这并不重要。
  • @kaedinger,是的,OP 只希望每个根对象只有一个唯一的子对象,因此无需返回 Collection
【解决方案2】:

除非您在数据存储中有某种可以使用的排序方式,否则您仍然必须“检索”项目来查看它们。如果你想要一个copy你的数据和结果而不是修改你的context数据,你需要某种克隆。所以在我看来——考虑到你的限制——最好只保留对生成的RootChild项目的引用:

var l = new List<Tuple<Root, Child>>();
foreach(var p in context.Roots.Include(x => x.Children))
{
    foreach(var c in p.Children)
    {
        if(c.Code == "A100")
        {
            l.Add(Tuple.Create(p, c));
            break;
        }
    }
}

这样,您只查看子项和根项一次,并且只检查子项,直到找到您的项。生成的元组列表包含对您各自的 RootChild 项目的引用而不修改它们,因此不要使用您引用的 Root 项目的 Children 属性。

【讨论】:

    猜你喜欢
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 2020-03-15
    • 2010-10-08
    • 1970-01-01
    相关资源
    最近更新 更多