简体   繁体   English

如何检测 EF 生成的多对多自引用关系中的引用循环?

[英]How can I detect reference loops in an EF-generated many-to-many self-reference relationship?

If, for example, I have the following object:例如,如果我有以下 object:

public class Foo
{
    public long Id { get; set; }

    public IEnumerable<Foo> FooParents { get; set; }
    public IEnumerable<Foo> FooChildren { get; set; }
}

How can I effectively detect a circular dependency for a Foo exemplar (not every circular dependency in DB) from a method within this exemplar?如何从该示例中的方法有效地检测Foo示例的循环依赖项(不是 DB 中的每个循环依赖项)? I know, what to do in case of one-to-many, but in case of many-to-many, I am a little bit confused.我知道,在一对多的情况下该怎么办,但在多对多的情况下,我有点困惑。 :( :(

Thank you!谢谢!

Here is an implementation with some tests .这是一个带有一些测试的实现

The interesting bit is here,有趣的一点就在这里,

public static bool IsCircularFoo(Foo foo)
{
    var soFar = new HashSet<long>();
    soFar.Add(foo.Id);
    if (foo.FooParents.Any(foo => IsCircularFooUp(foo, soFar)))
    {
        return true;
    }

    return foo.FooChildren.Any(foo => IsCircularFooDown(foo, soFar));
}

private static bool IsCircularFooUp(Foo foo, HashSet<long> soFar)
{
    if(soFar.Contains(foo.Id))
    {
        return true;
    }

    soFar.Add(foo.Id);
    return foo.FooParents.Any(foo => IsCircularFooUp(foo, soFar));
}

private static bool IsCircularFooDown(Foo foo, HashSet<long> soFar)
{
    if(soFar.Contains(foo.Id))
    {
        return true;
    }

    soFar.Add(foo.Id);
    return foo.FooChildren.Any(foo => IsCircularFooDown(foo, soFar));
}

The full working example is here.完整的工作示例在这里。

using System;
using System.Collections.Generic;
using System.Linq;
                    
public class Program
{
    public static void Main()
    {
        var soloFoo = new Foo
        {
            Id = 1,
            FooParents = Enumerable.Empty<Foo>(),
            FooChildren = Enumerable.Empty<Foo>(),
        };
        Console.WriteLine($"soloFoo isCircular:{IsCircularFoo(soloFoo)}");
        
        var selfParent = new Foo
        {
            Id = 1,
            FooChildren = Enumerable.Empty<Foo>(),
        };
        selfParent.FooParents = new[] { selfParent };
        Console.WriteLine($"selfParent isCircular:{IsCircularFoo(selfParent)}");
        
        var selfChild = new Foo
        {
            Id = 1,
            FooParents = Enumerable.Empty<Foo>(),
        };
        selfChild.FooChildren = new[] { selfChild };
        Console.WriteLine($"selfChild isCircular:{IsCircularFoo(selfChild)}");
        
        var parentFoo = new Foo
        {
            Id = 1,
            FooParents = Enumerable.Empty<Foo>(),
            FooChildren = Enumerable.Empty<Foo>(),
        };
        var childFoo = new Foo
        {
            Id = 2,
            FooParents = Enumerable.Empty<Foo>(),
            FooChildren = Enumerable.Empty<Foo>(),
        };
        var middleFoo = new Foo
        {
            Id = 3,
            FooParents = new[] { parentFoo },
            FooChildren = new[] { childFoo },
        };
        Console.WriteLine($"middleFoo isCircular:{IsCircularFoo(middleFoo)}");
        
        var ringFooA = new Foo
        {
            Id = 1,
        };
        var ringFooB = new Foo
        {
            Id = 2,
            FooParents = new[] { ringFooA },
        };
        var ringFooC = new Foo
        {
            Id = 3,
            FooParents = new[] { ringFooB },
            FooChildren = new[] { ringFooA },
        };
        ringFooA.FooParents = new[] { ringFooC };
        ringFooA.FooChildren = new[] { ringFooB };
        ringFooB.FooChildren = new[] { ringFooC };
        Console.WriteLine($"ringFooA isCircular:{IsCircularFoo(ringFooA)}");
        Console.WriteLine($"ringFooB isCircular:{IsCircularFoo(ringFooB)}");
        Console.WriteLine($"ringFooC isCircular:{IsCircularFoo(ringFooC)}");
    }
    
    public static bool IsCircularFoo(Foo foo)
    {
        var soFar = new HashSet<long>();
        soFar.Add(foo.Id);
        if (foo.FooParents.Any(foo => IsCircularFooUp(foo, soFar)))
        {
            return true;
        }

        return foo.FooChildren.Any(foo => IsCircularFooDown(foo, soFar));
    }

    private static bool IsCircularFooUp(Foo foo, HashSet<long> soFar)
    {
        if(soFar.Contains(foo.Id))
        {
            return true;
        }

        soFar.Add(foo.Id);
        return foo.FooParents.Any(foo => IsCircularFooUp(foo, soFar));
    }

    private static bool IsCircularFooDown(Foo foo, HashSet<long> soFar)
    {
        if(soFar.Contains(foo.Id))
        {
            return true;
        }

        soFar.Add(foo.Id);
        return foo.FooChildren.Any(foo => IsCircularFooDown(foo, soFar));
    }
}

public class Foo
{
    public long Id { get; set; }

    public IEnumerable<Foo> FooParents { get; set; }
    public IEnumerable<Foo> FooChildren { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 EF 6:多对多自引用:DELETE语句与REFERENCE约束冲突 - EF 6: many-to-many self-reference: The DELETE statement conflicted with the REFERENCE constraint 如何直接填充 EF Core 为多对多关系生成的连接表? - How can I directly fill join table generated by EF Core for many-to-many relationship? EF Core - 如何通过一对多关系自引用 model? - EF Core - How to self reference a model with one to many relationship? 通过带有EF的ID引用多对多关系 - Reference many-to-many relationships by ID with EF 引用现有记录或为EF中的多对多关系添加新记录 - Reference existing record or add new record for many-to-many relationship in EF 如何修改EF Core 5中多对多实体关系中生成的连接表 - How to modify the join table generated in many-to-many entity relationship in EF Core 5 多对多自我参考 - Many to Many Self Reference 实体框架核心2.2多对多自引用循环 - Entity Framework Core 2.2 Many-to-Many Self Reference Loop NHibernate自引用多对多,无需“向上引用” - NHibernate self-referencing many-to-many without “up reference” 在EF中没有联接表的情况下,如何在具有多对多关系的两个表(实体)之间使用LINQ进行JOIN查询? - How can I make a JOIN query with LINQ between two tables (entities) with many-to-many relationship, while there is no joining table in EF?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM