Понимание рекурсивной функции LINQ - PullRequest
0 голосов
/ 07 мая 2020

У меня есть следующая таблица

ItemNumber | FirstId | SecondId | ClientId
1          | 14      | 16       | NULL
2          | 17      | 18       | 1233242323
3          | 14      | 18       | 1233242323
5          | 15      | 12       | NULL
6          | 14      | 8        | 324234252
7          | 19      | 14       | 324234252
8          | 18      | 19       | 324234252
9          | 20      | 18       | 324234252

Со следующим классом

public class ClientObject
{
    public int ItemNumber { get; set; }
    public int FirstId { get; set; }
    public int SecondId { get; set; }
    public double ClientId { get; set; }

    public ClientObject()
    {

    }
}

Учитывая FirstId и ClientId, я хочу вернуть SecondId, если в столбце FirstId не найдено совпадений. Например: начиная с FirstId как 20 ItemNumber 9), я хотел бы вернуть 8 (ItemNumber 6).

Я пытаюсь использовать рекурсивный функциональный подход, но не уверен, правильно ли он или есть ли лучший способ решить эту проблему.

public ClientObject GetItemRecursive(int initialId, double client)
{
    var returnThis = databaseCtxt.TableToUse
        .Where(x => x.FirstId == initialId && x.ClientId == client)
        .AsEnumerable() // updated from suggestion 
        .Select(x => GetItemRecursive(x.SecondId, x.ClientId))
        .FirstOrDefault();
    return returnThis ;
}

Я попытался настроить это локально, но не смогли, так как это очень-очень маленькая часть большого проекта, поэтому я создал здесь самые маленькие примеры.

Ответы [ 2 ]

2 голосов
/ 08 мая 2020

Вам действительно нужно проверить, возвращает ли рекурсивный вызов null, означающий, что совпадений нет и вы попали в «лист».

public ClientObject GetItemRecursive(int initialId, double client)
{
    var initial = databaseCtxt.TableToUse
        .FirstOrDefault(x => x.FirstId == initialId && x.ClientId == client);
    if(initial == null) return null; 
    var child = GetItemRecursive(initial.SecondId, client);
    return child ?? initial;
}

Или вы можете написать его без рекурсии

public ClientObject GetItemRecursive(int initialId, double client)
{
    ClientObject parent = null;
    bool childFound = true;
    while(childFound)
    {
        var id = parent?.SecondId ?? initialId
        var child = databaseCtxt.TableToUse
            .FirstOrDefault(x => x.FirstId == id && x.ClientId == client);
        if(child == null) childFound = false;
        else parent = child;
    }

    return parent;
}
0 голосов
/ 08 мая 2020

Я предполагаю, что вы ищете что-то вроде этого

public ClientObject GetItemRecursive(int initialId, double client)
{
    return Inner(initialId, client, null);

    ClientObject Inner(int id, double d, ClientObject acc )
    {
       var current = databaseCtxt.TableToUse
         .Where(x => x.FirstId == id&& x.ClientId == d)
         .FirstOrDefault();
       if(current == null) return acc;    
       return Inner(current.SecondId, current.ClientId, current);
    }
}

Этот код использует C# 7.0 локальные функции для удобства.

Итак, общий подход заключается в том, что у вас есть аккумулятор (acc), который обновляется на каждой итерации, пока вы не сможете найти продолжение (запрос к db возвращает null), когда вы достигнете этой точки, вы вернете аккумулятор через все вложенные вызовы.

...