web-dev-qa-db-ja.com

式ツリーをシリアル化する

私はC#で分散システムを実行していますが、障壁に遭遇しました。

タイプで述語をシリアル化できる必要があります

Predicate<ICollection<IEntity>> p = (entities => entities.OfType<Person>().Count() <= 3);

これは.netでは不可能だと私は信じているので、私の質問は、そのトリックを実行できるフレームワークが存在するかどうかです。

私はすでにいくつかのフレームワークを試しましたが、コレクションまたはリストを受け取る述語をシリアル化できないという問題に直面し続けています

誰かが解決策を知っていることを願っています。ここ数週間、この問題に悩まされています...

22
cholewa1992

私の解決策:

長い間問題を解決した後、ついにjson.netとAq.ExpressionJsonSerializerを使用して私の問題を解決することができました( https://github.com/aquilae/expression-json-serializer

public class JsonNetAdapter : IOconSerializer
{
    private readonly JsonSerializerSettings _settings;

    public JsonNetAdapter(JsonSerializerSettings settings = null)
    {
        var defaultSettings = new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Objects};
        defaultSettings.Converters.Add(new ExpressionJsonConverter(Assembly.GetAssembly(typeof(IOconSituation))));
        _settings = settings ?? defaultSettings;
    }

    public string Serialize<T>(T obj)
    {
        return JsonConvert.SerializeObject(obj, _settings);
    }

    public T Deserialize<T>(string json)
    {
        return JsonConvert.DeserializeObject<T>(json, _settings);
    }
}

チャームのように機能します!

13
cholewa1992

私は以前にこれを試みました。多少の作業が必要ですが、ネットワークを介して述語を渡すための独自のプロトコルを開発できます。

まず、p変数のタイプを_Expression<TDelegate>_に変更して、分解できるようにする必要があります。

_Expression<Predicate<ICollection<IEntity>>> p = (entities => entities.OfType<Person>().Count() <= 3);

VisitExpression(p);
_

C#コンパイラは、ラムダを_Expression<TDelegate>_変数に割り当てていることを認識し、実際に式ツリーを構築します。

これで、式ツリーをウォークして、カスタムプロトコルにシリアル化できます。ここではStringBuilderを使用して、JSONオブジェクトを作成します(逆シリアル化を容易にするため)。

_StringBuilder sb = new StringBuilder();

void VisitExpression(Expression e)
{
    switch (e.ExpressionType)
    {
    case ExpressionType.And:
        return VisitBinaryExpression(e As BinaryExpression);

    ...
    }
}

void VisitBinaryExpression(BinaryExpression e)
{
    sb.AppendLine("{");
    switch (e.ExpressionType)
    {
    case ExpressionType.And:
        sb.Append("\"Type\": \"And\",");
        break;

    ...
    }
    sb.Append("\"Left\":");
    VisitExpression(e.Left); sb.Append(",");
    sb.Append("\"Right\":");
    VisitExpression(e.Right);
    sb.AppendLine("}");
}
_

分散システムがコレクションとリストを処理する方法に応じて、式ツリーをウォークするときに対応するロジックを実装する必要があります。 typeof(IEnumerable<>).MakeGenericType(typeof(IEntity)).IsAssignableFrom(typeToTest)を使用することから始めます。

シリアル化するときは、タイプ、メソッド、およびオーバーロードのフルネームをネットワーク全体に送信する必要があります。すべてを逆シリアル化するときにタイプとメソッドを正しく解決できるように、各計算ノードがすべて同じライブラリを参照していることを確認することをお勧めします。

最終的に逆シリアル化したら、_System.Linq.Expressions_名前空間のクラスを使用してリモートホストの式ツリーを再構築します。次に、Lambda.Compile()を使用して式をコンパイルして実行します。

8
Josh Wyant