I've attempted this before. It will take some work, but you can develop your own protocol to pass predicates across a network.
First, you need to change the type of your p
variable to an Expression<TDelegate>
so it can be deconstructed:
Expression<Predicate<ICollection<IEntity>>> p = (entities => entities.OfType<Person>().Count() <= 3);
VisitExpression(p);
The C# compiler will see that you are assigning a lambda to an Expression<TDelegate>
variable, and it will actually build an expression tree for you.
Now, you can walk the expression tree and serialize it to your custom protocol. I'll use a StringBuilder
here, to create a JSON object (for easy deserialization).
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("}");
}
Depending on how your distributed system handles collections and lists, you will need to implement the corresponding logic when walking the expression tree. I would start by using typeof(IEnumerable<>).MakeGenericType(typeof(IEntity)).IsAssignableFrom(typeToTest)
.
When serializing, you will have to send the full names of the types, methods, and overloads across the network. You'll probably want to make sure that each compute node is referencing all the same libraries, so that you can correctly resolve the types and methods when you deserialize everything.
When you finally deserialize, rebuild the expression tree on the remote host using the classes in the System.Linq.Expressions
namespace. Then, compile and run the expression using Lambda.Compile()
.