Dynamic Linqでのメモリ内フィルタリングが必要です。私のオブジェクトにはインデクサーしかありません:
public object this[int index] { }
私のデータへのアクセスは次のようなものです:オブジェクト[0]、オブジェクト[1]、...
だから私のクエリは次のようなものです:
// get FilterText from user at runtime
// eg. filterText can be: [0] > 100 and [1] = "wpf"
collection.AsQueryable().where(filterText);
これを行う方法はありますか?
お知らせします。
それは実際に可能です...しかし! (はい、ありますが)。
動的Linqライブラリを使用していると思います。次に、「it」キーワードを使用して、インデックス作成操作を適用する現在のオブジェクトを指定できるようにする必要があります。
ただし...インデクサーの戻りデータ型はオブジェクトであるため、データ型が一致しないため、[0]> 100および[1] = "wpf"を書き込むことができません。
また、DynamicObjectから派生し、実行時にプロパティを追加する場合でも、それらのプロパティは、現在の状態の動的linqライブラリによって実行時に解決されません。タイプxxxに存在しないフィールドまたはプロパティを取得するだけです。
これにはいくつかの解決策があり、そのうちいくつかは解決策として受け入れることができます。
醜い解決策の1つは、データ型の数が限られている場合、たとえばn(n <.NETの型の数)の場合、n個のインデクサーをパラメーターマッチングで使用して、必要なデータ型を取得できます。たとえば、ほとんどがintでいくつかの文字列がある場合:
it[0] > 100 AND it[1, "dummy"] = "wpf" //The dummy parameter allows you to specify return type.
別の醜い解決策である動的LinqはToString()とConvert-methodsの使用をサポートしているため、たとえば上記と同じクエリを記述できます。
Convert.ToDouble(it[0]) > 100 AND it[1].ToString() = "wpf".
3番目の醜い解決策は、データを変換する方法を示す方法でステートメントをラップする規則を使用することです。たとえば、次のように書くことができます。
it[0] > 100 AND it{1} = "wpf"
そして、「it [」を「Convert.ToDouble(it [」などに置き換えます...
私が正しければ、ライブラリでジェネリックインデクサーを使用することもできないと思います。また、この場合、戻り値の型がオブジェクトであるため、Convert.ChangeTypeは役に立ちません。
たぶん私や他の誰かがこの種のことをサポートするためにライブラリを書き直すかもしれませんが、近い将来(数週間)にはそうする時間はありません。
まあ、申し訳ありませんが、15分以内にどこかに行かなければならないので、後でもっと良い解決策をとらなければなりません。
会議に自分をテレポートする
更新:
私はあなたの(そして私の)問題の解決策を見つけたかもしれません!
DLINQライブラリでは、IxxxSignaturesインターフェイスで、操作したいタイプのメンバーを追加できます。
したがって、(たとえば)IEqualitySignaturesに追加しました:
void F(Object x, Int32 y);
そして、このようにelseブロックの(この場合)ParseComparisonメソッドを変更しました。
left = Expression.Convert(left, right.Type);
そして、信じられないかもしれませんが、うまくいきました:)
他のタイプと操作のシグネチャを追加していないため、すべての種類の操作をテストしたわけではありませんが、簡単に実行できるはずです!
更新
上記のいくつかのマイナーなものを更新しました。
私はこれについてもう少し実験しています、そしてそれは最もきれいな解決策ではないかもしれませんが、あなたはこのようなことをすることができます(DataObjectはインデクサー付きの単なるDynamicObjectです):
[TestMethod]
public void DynamicTest()
{
List<DataObject> dataObjects = new List<DataObject>();
dynamic firstObject = new DataObject();
dynamic secondObject = new DataObject();
firstObject.dblProp = 10.0;
firstObject.intProp = 8;
firstObject.strProp = "Hello";
secondObject.dblProp = 8.0;
secondObject.intProp = 8;
secondObject.strProp = "World";
dataObjects.Add(firstObject);
dataObjects.Add(secondObject);
/* Notice the different types */
string newQuery = FormatQuery("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'");
var result = dataObjects.Where(newQuery);
Assert.AreEqual(result.Count(), 1);
Assert.AreEqual(result.First(), firstObject);
}
そして、(私が完全なメソッドを書いたとしましょう)のようなある種のフォーマットメソッド:
public string FormatQuery(string query)
{
query = query.Replace('\'', '\"');
string[] operators = new string[] { "<", ">", "!=", "<=", ">=", "<>", "=" };
string[] parts = query.Split();
for (int i = 0; i < parts.Length; i++)
{
if (operators.Contains(parts[i]))
{
parts[i - 1] = "it[\"" + parts[i - 1] + "\"]";
}
}
return String.Join(" ", parts);
}
もちろん、代わりに拡張メソッドを使用することもできます。
または...次のような方法を使用してメソッドをDLINQ libに配置することもできます(ただし、これは良いアイデアとは言えません)。
if (typeof(T).GetInterface("IDynamicMetaObjectProvider", true) != null)
whereClause = whereClause.FormatQuery();
そして、型が文字列インデクサーを実装しているかどうかを確認します(たとえば、ここではIndexerName属性を無視します)。
if (t.GetType().GetProperty("Item") != null)
これにより、「通常のユーザー」が次のように記述できます。
data.Where("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'")
まあ、そこにそのようなものがあるのは良くないかもしれませんが、あなたは要点を理解します。あなたが持つことができます! :)
あなただけであなたのインデクサーを付加する必要がありit
にDynamicLinq言語で同等であるthis
C#で。
したがって、必要なのはit[1] == "wpf"
だけですit[1] == "wpf"
。
ただし、インデクサーがobject
返すため、さらに複雑になりobject
。クラス型( string
を含む)の場合は問題ありません。DLinqは必要に応じてすべてを昇格します。
ただし、 int
のような値型の場合、 Int32(it[0]) > 10
を実行する必要があります。