递归的LINQ查询

3
我有以下对象结构。
public class Study 
{
    public Guid? PreviousStudyVersionId { get; set; }
    public Guid StudyId { get; set; }
    //Other members left for brevity
}

它使用Entity Framework Code First进行持久化。

这会导致像这样的表格:

PreviousStudyVersionId                  StudyId
EF90F9DC-C588-4136-8AAE-A00E010CE87B    E4315CFD-9638-4225-998E-A00E010CEEEC
NULL                                    1C965285-788A-4B67-9894-3D0D46949F11
1C965285-788A-4B67-9894-3D0D46949F11    7095B746-8D32-4CC5-80A9-A00E010CE0EA
7095B746-8D32-4CC5-80A9-A00E010CE0EA    EF90F9DC-C588-4136-8AAE-A00E010CE87B

现在我想要递归查询所有studyId。所以我想到了以下解决方案:
当我在我的repository中调用GetAllStudyVersionIds(new Guid("7095B746-8D32-4CC5-80A9-A00E010CE0EA"))方法时,它会返回给我所有的4个studyId。
    public IEnumerable<Guid> GetAllStudyVersionIds(Guid studyId)
    {
        return SearchPairsForward(studyId).Select(s => s.Item1)
            .Union(SearchPairsBackward(studyId).Select(s => s.Item1)).Distinct();
    }

    private IEnumerable<Tuple<Guid, Guid?>> SearchPairsForward(Guid studyId)
    {
        var result =
            GetAll().Where(s => s.PreviousStudyVersionId == studyId).ToList()
            .Select(s => new Tuple<Guid, Guid?>(s.StudyId, s.PreviousStudyVersionId));
        result = result.Traverse(a => SearchPairsForward(a.Item1));
        return result;
    }

    private IEnumerable<Tuple<Guid, Guid?>> SearchPairsBackward(Guid studyId)
    {
        var result = GetAll().Where(s => s.StudyId == studyId).ToList()
            .Select(s => new Tuple<Guid, Guid?>(s.StudyId, s.PreviousStudyVersionId));
        result = result.Traverse(a => a.Item2.HasValue ? SearchPairsBackward(a.Item2.Value) : Enumerable.Empty<Tuple<Guid, Guid?>>());
        return result;
    }

这是我扩展方法的实现。
public static class MyExtensions
{
    public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
    {
        foreach (var item in source)
        {
            yield return item;
            var seqRecurse = fnRecurse(item);
            if (seqRecurse == null) continue;
            foreach (var itemRecurse in Traverse(seqRecurse, fnRecurse))
            {
                yield return itemRecurse;
            }
        }
    }
}

有没有办法将这个代码更靠近数据库(IQueryable),并优化它?


你可能最终需要编写一个SQL查询。不同的数据库平台可能会有所不同。MS SQL Server可以使用递归CTE来实现这一点。 - vcsjones
从列的名称推测,您正在有效地创建一个历史项目的链表。可能有一种不同的存储方式更适合关系模型,例如拥有一个研究表和一个引用它的历史项目表。 - OlduwanSteve
1个回答

1

我通过创建一个递归表函数拉取所有基于 id 的父记录,然后创建视图以生成每个记录和所有父记录的列表来完成此操作。然后在EF模型中使用该视图,并关联以便能够在LINQ中使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接