Home > Back-end >  Serialize/Deserialize Expression<Func<T, bool>>[] using Serialize.Linq
Serialize/Deserialize Expression<Func<T, bool>>[] using Serialize.Linq

Time:01-16

Coming from this question, I have almost the same need.

I want to serialize an array of predicates (Expression<Func<T, bool>>[]) to send it over a gRPC connection.

On the server side, I'm looping through each predicate from the array to apply a WHERE clause on an IQueryable :

foreach (var predicate in predicates)
{
    items = items.Where(predicate);
}

So my method's signature looks like that : public Task<List<T>> Get(params Expression<Func<T, bool>>[] predicates)

Using the Serialize.Linq library, I serialize my array of predicates like that :

var serializedPredicates =
    predicates
    .Select(x => _serializer.SerializeText(x));
var request = new GetRequest()
{
    Filters = JsonSerializer.Serialize(serializedPredicates)
};

So I use the library to create a list of string (serialized expressions), then I serialize this list using the usual System.Text.Json library to send it over gRPC.

But I'm not able to deserialize this list on the server side.

Here is the code of the deserialization :

var serializedPredicates = 
    JsonSerializer
    .Deserialize<IEnumerable<string>>(request.Filters);

var predicates =
    serializedPredicates
    .Select(x => _serializer.DeserializeText(x))
    .ToArray();

var result = await service.Get(predicates);

So I take the request.Filters and deserialize it as a list of string, then for each string I deserialize it using the Serialize.Linq library (_serializer.DeserializeText(x)) and apply .ToArray() to get an array of Expression.

Unfortunately, I can only get a Expression[] using this method (which is already kinda ugly), but I'm not having a true Expression<Func<T, bool>>[] like my method is awaiting.

So it obviously results in an error :

Exception was thrown by handler. RuntimeBinderException: The best overloaded method match for 'Tests.Repository<DataModel.TestData>.Get(params System.Linq.Expressions.Expression<System.Func<DataModel.TestData,bool>>[])' has some invalid arguments

Because I'm using gRPC, I can't create a generic service. It would have been easier to cast the deserialization like that :.Select(x => (Expression<Func<T, bool>>) _serializer.DeserializeText(x)) but I don't know the type of T before the running time.

I also tried using

var predicatesType = typeof(Expression<>)
    .MakeGenericType(Expression.GetFuncType(requestType, typeof(bool)));
...
.Select(x => Convert.ChangeType(_serializer.DeserializeText(x), predicatesType))

where the requestType is sent over gRPC too, to retrieves the Type in a variable. But unfortunately the Expression does not seem to be convertible :

Exception was thrown by handler. InvalidCastException: Object must implement IConvertible

How can I properly serialize a Expression<Func<T, bool>>[] and deserialize it as a proper Expression<Func<T, bool>>[] ?

Thanks for your help.

CodePudding user response:

Possibly I'm missing something obvious here, but assuming that you have all the required types on the client side (otherwise I doubt this should work at all) - have you tried just casting the expressions to the required type:

var predicates = serializedPredicates
    .Select(x => _serializer.DeserializeText(x))
    .Cast<Expression<Func<DataModel.TestData, bool>>>()
    .ToArray();

If needed - move the casting to helper method (or use Enumerable.Cast itself) and invoke it dynamically via reflection.

  • Related