Serializar árbol de expresiones

c# expression-trees lambda serialization

Pregunta

Estoy haciendo un sistema distribuido en c # y he encontrado una barrera.

Necesito poder serializar Predicado con tipo

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

Creo que esto no es posible en .net, por lo que mi pregunta es si existe algún marco que pueda hacer el truco.

Ya probé un par de marcos, pero sigo encontrando el problema de que no pueden serializar predicados que toman una colección o lista

Espero que alguien sepa una solución. Se han quedado estancados con este problema durante un par de semanas ...

Respuesta aceptada

Mi solución:

Después de dejar de lado el problema durante mucho tiempo, finalmente logré resolver mi problema usando json.net y 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);
    }
}

¡Funciona de maravilla!


Respuesta popular

He intentado esto antes. Tomará algo de trabajo, pero puede desarrollar su propio protocolo para pasar predicados a través de una red.

Primero, necesita cambiar el tipo de su variable p a una Expression<TDelegate> para que se pueda deconstruir:

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

VisitExpression(p);

El compilador de C # verá que estás asignando un lambda a una variable de Expression<TDelegate> , y en realidad construirá un árbol de expresiones para ti.

Ahora, puede recorrer el árbol de expresiones y serializarlo a su protocolo personalizado. StringBuilder un StringBuilder aquí, para crear un objeto JSON (para una fácil deserialización).

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("}");
}

Dependiendo de cómo su sistema distribuido maneja las colecciones y las listas, deberá implementar la lógica correspondiente al recorrer el árbol de expresiones. Comenzaría utilizando typeof(IEnumerable<>).MakeGenericType(typeof(IEntity)).IsAssignableFrom(typeToTest) .

Al serializar, deberá enviar los nombres completos de los tipos, métodos y sobrecargas a través de la red. Probablemente querrá asegurarse de que cada nodo de cómputo haga referencia a las mismas bibliotecas, de modo que pueda resolver correctamente los tipos y métodos cuando deserializa todo.

Cuando finalmente deserialice, vuelva a generar el árbol de expresiones en el host remoto utilizando las clases en el espacio de nombres System.Linq.Expressions . Luego, compile y ejecute la expresión utilizando Lambda.Compile() .



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow