Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send lambda expression from client to server

I have a method that helps to dynamically build a query on a client:

public virtual IList<TEntity> Fill(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
{
    includeProperties = includeProperties ?? "";

    var qctx = new TQueryContext
    {
        QueryType = filter == null ? CommonQueryType.FillAll : CommonQueryType.FillWhere,
        Filter = filter,
        OrderBy = orderBy
    };

    qctx.Includes.AddRange(includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));

    _detachedServerRepo.Read(qctx);

    return qctx.Entities;
}

I want to send qctx to a server repo which might be on another machine. Since a TQueryContext will be typed from QueryContextBase defined in part as below I can't serialize it.

public class QueryContextBase<TEntity, TKey>
    where TEntity : StateTrackedObject
    where TKey : IEquatable<TKey>
{
    public TKey ID { get; set; }
    public string Alf { get; set; }
    public List<TEntity> Entities { get; set; }
    public List<string> Includes { get; set; }
    public Expression<Func<TEntity, bool>> Filter { get; set; }
    public Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> OrderBy { get; set; }
}

How can I create similar properties to Filter and OrderBy so I can serialize them and then build up the query in the server repo as below:

protected override void FillWhere(TQueryContext qctx)
{
    qctx.Entities.AddRange(this.Fill(qctx.Filter, qctx.OrderBy,
    qctx.GetIncludesAsString()));
}

protected override void FillAll(TQueryContext qctx)
{
    qctx.Entities.AddRange(this.Fill(null, qctx.OrderBy, qctx.GetIncludesAsString()));
}

public virtual IEnumerable<TEntity> Fill(Expression<Func<TEntity, bool>> filter = null,
            Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
            string includeProperties = "")
{
    includeProperties = includeProperties ?? "";

    try
    {
        IQueryable<TEntity> querySet = DbSet;

        if (filter != null)
        {
            querySet = querySet.Where(filter);
        }

        foreach (var includeProperty in includeProperties.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
        {
            querySet = querySet.Include(includeProperty.Trim());
        }

        return (orderBy == null) ? querySet.ToList() : orderBy(querySet).ToList();
    }
    catch (Exception ex)
    {
        return ex.ThrowDalException<IEnumerable<TEntity>>(OperationType.Read, ex.Message, ex);
    }
}
like image 877
lacksInitiative Avatar asked Feb 02 '26 03:02

lacksInitiative


1 Answers

You're right not to want to reinvent the wheel. Check out Serialize.Linq.

You could solve your problem with this library as below:

In your client repo:

public virtual IList<TEntity> Fill(Expression<Func<TEntity, bool>> filter = null,
        Expression<Func<IQueryable<TEntity>, 
        IOrderedQueryable<TEntity>>> orderBy = null,
        string includeProperties = "") {

        includeProperties = includeProperties ?? "";
        try
        {
            var qctx = new TQueryContext
            {
                QueryType = filter == null ? CommonQueryType.FillAll : CommonQueryType.FillWhere,
                FilterNode = filter == null ? null : filter.ToExpressionNode(),
                OrderByNode = orderBy == null ? null : orderBy.ToExpressionNode()
            };

And then in your QueryContext just add the extra property and convert:

 public ExpressionNode FilterNode { get; set; }

 public Expression<Func<TEntity, bool>> Filter 
 { 
    get {
        return FilterNode == null ? null : FilterNode.ToBooleanExpression<TEntity>();
    } 
 }

 public ExpressionNode OrderByNode { get; set; }

 public Expression<Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>> OrderBy 
 {
    get {
        return OrderByNode ==  null ? null : OrderByNode.ToExpression<Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>>();
    }
 }
like image 50
rism Avatar answered Feb 04 '26 17:02

rism



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!