Consulta de linq recursiva

I have the following object structure.

public class Study 
{
    public Guid? PreviousStudyVersionId { get; set; }
    public Guid StudyId { get; set; }
    //Other members left for brevity
}

It is persisted using entity Framework code first.

It results in a table like this

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

Now I want to query all studyId's recursive. So I came up with the following solution:

So when I call the method in my repository GetAllStudyVersionIds(new Guid("7095B746-8D32-4CC5-80A9-A00E010CE0EA")) it returns me all 4 the studyId's.

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

This is the implementetion of my extension method.

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

Is there any way of moving this closer to the database (IQueryable) and optimize this code.

preguntado el 09 de marzo de 12 a las 13:03

You may end up having / wanting to write a SQL query instead. Different database platforms can do this differently. MS SQL Server can do this with a CTE recursivo. -

Guessing from the names of the columns you are effectively creating a linked list of history items. There may be a way of storing this differently that fits better with the relational model e.g. having a study table, and a table full of history items which refer to it. -

1 Respuestas

I have done this by making a recursive table function pulling all parent records based on id and then creating a view to produce a list for each record and all the parents. Then I consume that in the EF model and associate to be able to use with LINQ.

respondido 09 mar '12, 13:03

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.