Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting next and previous on a Queryable

Tags:

c#

linq

Yo what up gangstas?

I have the following code which seems to return null always.

    public static T GetNext<T>(this IQueryable<T> list, T current)
    {
        try
        {
            return list.SkipWhile(x => !x.Equals(current)).Skip(1).First();
        }
        catch
        {
            return default(T);
        }
    }

    public static T GetPrevious<T>(this IQueryable<T> list, T current)
    {
        try
        {
            return list.TakeWhile(x => !x.Equals(current)).Last();
        }
        catch
        {
            return default(T);
        }
    }

Given a Queryable from a data source and a record in it, I just want to get the next and previous values.

like image 465
DarthVader Avatar asked Jun 01 '26 11:06

DarthVader


2 Answers

The cause is that SkipWhile is not supported by EntityFramework.

First, to use Skip or Take the collection must be some in order to make EntityFramework support that methods. So, before method call, you need to have OrderBy method:

 var orderedList = list.OrderBy(elem => elem.Id) // or other property 
                                                // but need to be rememebered
                                                // because it will be used in furher

Then your method for next could be sth like this:

public static T GetNext<SomeEntity>(
            this IOrderedQueryable<SomeEntity> list,
            SomeEntity current)
{
    return list.Where(elem => elem.Id > current.Id)
               .FirstOrDefault(); // faster than try-catch
     // assuming, that Id is unique
}

That method has one drawback. It is not generic. But with a little effort,you can prepare a generic version:

public static T GetNext<T>(this IOrderedQueryable<T> list, T current, string orderProperty)
{
    var predicate = string.Format("{0} > @0", orderProperty);
    var propValue = current.GetType()
                           .GetProperty(orderProperty, 
                                        BindingFlags.Public | BindingFlags.Instance)
                           .GetValue(current);
    return (T)list.Where(predicate, propValue).FirstOrDefault();
                  //where comes from System.Linq.Dynamic
}

If you don't like strings as propery names:

public static T GetNext<T>(this IOrderedQueryable<T> list, 
                          T current, 
                          Func<T, object> orderProperty)
{
    ..
    var propValue = orderProperty(current);
    ..
}

And usage:

orderedList.GetNext(current, orderProperty: elem => elem.Id);

The generic version relays on System.Linq.Dynamic that can be found on codeplex

I hope it gives you some clue how to make your code work or some idea how to implement it with other approach.

Maybe I will get crucified for this solution (because I find it kind of dirty)... but... it works (and it's not using TakeWhile or SkipWhile or DynamicLINQ):

public static T GetNext<T>(this IQueryable<T> list, T current)
{
    try
    {
        int idx = 0;
        foreach (T item in list)
        {
            if (!item.Equals(current))
            {
                idx++;
            }
            else
            {
                break;
            }
        }

        return list.Skip(idx).First();
    }
    catch
    {
        return default(T);
    }
}

public static T GetPrevious<T>(this IQueryable<T> list, T current)
{
    try
    {
        int idx = 0;
        foreach (T item in list)
        {
            if (!item.Equals(current))
            {
                idx++;
            }
            else
            {
                break;
            }
        }
        if (idx - 1 == 0)
            return list.First();
        return list.Skip(idx - 1).First();
    }
    catch
    {
        return default(T);
    }
}
like image 29
stefankmitph Avatar answered Jun 03 '26 01:06

stefankmitph



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!