Running sum using the Dynamic Linq library

c# dynamic-linq linq

Question

Given the following list:

var items = new List<int> { 1, 2, 3, 5 };

I am trying to calculate a total of previous records for any given record using the Dynamic Linq library. In standard LINQ (LINQ to Objects), I could do this:

var runningTotal = 0;
var qry1 = items.Select(x => {
    var ret = new { x, runningTotal };
    runningTotal += x;
    return ret;
});

or this (less efficient, because it's iterating over all the previous items in the list each time. This is obviously not an issue for such a small list, but it will be for larger ones):

var qry1 = items.Select((x, idx) => new { 
    x, 
    runningTotal = items.Take(idx).Sum()
});

The library doesn't implement Take within a string expression, or an overload of Select that takes the index of the element. However, the following almost gets there:

var qry2 = items.AsQueryable().Select(
    "new(it as x, @0.Where(it < parent).Sum(it) as runningTotal)",
    items
);

However, this suffers from a few shortcomings:

  • The list must have some order to it, or else the inner Where will return arbitrary records
  • For the same reason, each item must be unique
  • This suffers from the same inefficiency as the second C# example

Is there any way to do this using Dynamic Linq, which doesn't have these problems, and if yes then how?


Update

I tried writing the following class:

[DynamicLinqType]
public class Aggregator<T> {
    private T state;
    private Func<T, T, T> fn;
    public Aggregator(Func<T, T, T> fn) {
        this.fn = fn;
    }
    public T GetState(T value) {
        state = fn(state, value);
        return state;
    }
}

and then querying like this:

var aggregator = new Aggregator<int>((runningTotal1, x) => runningTotal1 + x);
var qry4 = items.AsQueryable().Select(
    "new(it as x, @0.GetState(it) - it as runningTotal)", 
    aggregator
);

but I get a ParseException: Methods on type 'Aggregator1' are not accessible even though the GetState method is public. This is because the library restricts itself to using only members of certain predefined types, as well as types marked with DynamicLinqTypeAttribute (by default). But the marked type Aggregator<T> is not the same as the constructed type (Aggregator<int>).


Update 2

I have filed an issue about running calculations and another issue about generic combinations of recognized types.

1
3
11/7/2015 9:57:27 PM

Popular Answer

I do a bit research and found where is error:

DynamicLinqType register type - in your case Aggregator<T>

So when you specify T to int this already another type Aggregator<int>, and DynamicLinq think that it not predifined type.

So as a solution you can just remove generic part:

[DynamicLinqType]
public class Aggregator
{
    private int state;
    private Func<int, int, int> fn;
    public Aggregator(Func<int, int, int> fn)
    {
        this.fn = fn;
    }
    public int GetState(int value)
    {
        state = fn(state, value);
        return state;
    }
}

UPDATE:
yet another way - fix this in source:

static bool IsPredefinedType(Type type)
{
    if (_predefinedTypes.Contains(type)) return true;

    if (GlobalConfig.CustomTypeProvider.GetCustomTypes().Contains(type)) return true;

    // for generic type check GenericTypeDefinition
    if (type.IsGenericType && GlobalConfig.CustomTypeProvider.GetCustomTypes().Contains(type.GetGenericTypeDefinition())) return true;

    return false;
}
3
11/6/2015 8:51:24 AM


Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow