次のリストがあるとします。
var items = new List<int> { 1, 2, 3, 5 };
Dynamic Linqライブラリを使用して、特定のレコードの以前のレコードの合計を計算しようとしています 。標準のLINQ(LINQ to Objects)では、次のようにすることができます。
var runningTotal = 0;
var qry1 = items.Select(x => {
var ret = new { x, runningTotal };
runningTotal += x;
return ret;
});
またはこれ(リストの前のすべての項目を毎回繰り返すため、効率が低下します。これは明らかにこのような小さなリストでは問題ではありませんが、大きなリストでは問題になります):
var qry1 = items.Select((x, idx) => new {
x,
runningTotal = items.Take(idx).Sum()
});
ライブラリは、文字列式内にTake
実装しないか、要素のインデックスを取得するSelect
オーバーロードを実装しません。ただし、次の方法でほぼ達成できます。
var qry2 = items.AsQueryable().Select(
"new(it as x, @0.Where(it < parent).Sum(it) as runningTotal)",
items
);
ただし、これにはいくつかの欠点があります。
Where
、内部のWhere
は任意のレコードを返しますこれらの問題がないDynamic Linqを使用してこれを行う方法はありますか?
更新
私は次のクラスを書いてみました:
[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;
}
}
そして、このようにクエリします:
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
);
しかし、 ParseException: Methods on type 'Aggregator1' are not accessible
メソッドは、 GetState
メソッドがパブリックであってもParseException: Methods on type 'Aggregator1' are not accessible
。これは、ライブラリが、特定の事前定義されたタイプのメンバーだけでなく、 DynamicLinqTypeAttribute
(デフォルトで)でマークされたタイプのメンバーの使用のみに制限されているためです。ただし、マークされた型Aggregator<T>
は、構築された型( Aggregator<int>
)と同じではありません。
アップデート2
計算の実行に関する問題と、認識された型の一般的な組み合わせに関する別の問題を提出しました。
私は少し調べて、エラーがどこにあるかを見つけました:
DynamicLinqType
レジスタタイプ-あなたの場合はAggregator<T>
したがって、 T
をintに指定すると、これはすでに別の型Aggregator<int>
であり、 DynamicLinq
はそれを事前定義された型ではないとDynamicLinq
ます。
したがって、解決策として、一般的な部分を削除することができます:
[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;
}
}
更新:
さらに別の方法-ソースでこれを修正:
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;
}