繁体   English   中英

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

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

例如,如果我有以下 object:

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

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

如何从该示例中的方法有效地检测Foo示例的循环依赖项(不是 DB 中的每个循环依赖项)? 我知道,在一对多的情况下该怎么办,但在多对多的情况下,我有点困惑。 :(

谢谢!

这是一个带有一些测试的实现

有趣的一点就在这里,

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

完整的工作示例在这里。

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM