如何检索每个仅包含一个孩子的记录列表?

时间:2019-05-20 06:59:13

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

示例模型。

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

约束。

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

示例查询

获取代码为A100的所有带有Child的根记录。

包含两个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记录及其所有子级。然后,迭代每个记录,并删除所有与我要查询的代码不同的子级。这种方法的问题在于,当数据库增长时,它将对这种方法产生影响,因为当我需要的每个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();

在我的情况下,使用构造函数也不理想,因为我在实例化过程中将每个对象的状态设置为已创建,这是每个模型设计的一部分。

编辑1

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

编辑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();

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

2 个答案:

答案 0 :(得分:1)

除非可以在数据存储中进行某种排序,否则仍然需要“检索”项目才能对其进行查看。而且,如果您希望将数据与结果进行复制而不是修改您的 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项目的引用,而无需对其进行修改,因此请不要使用所引用的Children项目的Root属性。

答案 1 :(得分:1)

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

__init__