New Posts New Posts RSS Feed: Serializing SortSelector
  FAQ FAQ  Forum Search   Calendar   Register Register  Login Login

Serializing SortSelector

 Post Reply Post Reply
Author
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Topic: Serializing SortSelector
    Posted: 29-Dec-2011 at 2:35pm
I have a server method that takes a sort selector and an Expression<Func<EntityType,bool>> and I've noticed that both of these cannot be serialized. What is the correct way to do this? Simply adding it to known type does not work.

Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 30-Dec-2011 at 11:16am
We don't make this easy, which is by accident not design. 
 
Internally, DevForce creates an EntityQuerySurrogate when sending a query to the server.  This is essentially a wrapper over the query expression, along with some means of rehydrating the query.  Unfortunately, this also has an internal constructor. 
 
You can, though, do some of what the surrogate does yourself using the IdeaBlade.Linq.SerializedExpression class.  You need to add this type as a known type on both client and server.  You can create a SerializedExpression for a whole query, or a portion of it, as you want here.  It's a bit tricky to use.
 
To build SerializedExpressions for your query parts you can do something like this: 
 
var p1 = PredicateBuilder.Make<Product>(p => p.Discontinued == false);
var exp1 = SerializedExpression.ToSerializedExpression(p1);
 
var ss = new SortSelector(typeof(Product), "ProductName");
var exp2 = SerializedExpression.ToSerializedExpression(ss.ToLambdaExpression());
 
You'd then pass the SerializedExpressions to the server method, which could build a query as follows:
 
var p1 = args[0] as SerializedExpression;  // where
var p2 = args[1] as SerializedExpression;   // sort
 
var exp1 = p1.ToExpression() as Expression<Func<Product, bool>>;
var exp2 = p2.ToExpression() as Expression<Func<Product, string>>;
var query = em.GetQuery<Product>().Where(exp1).OrderBy(exp1);
 
As you can see, it's a bit messy rehydrating the expresions.  If you don't know the entity type, you can pass that with the IdeaBlade.Core.TypeWrapper class, which is specifically designed for serializing type information. 
 
You could instead build up the dynamic query on your client and pass the query expression to your server method.  It would look something like this:
 
var p1 = PredicateBuilder.Make<Product>(p => p.Discontinued == false);
var ss = new SortSelector(typeof(Product), "ProductName");
var query = em.Products.Where(p1).OrderBySelector(ss);
var exp = SerializedExpression.ToSerializedExpression(query.Expression);
 
On the server you'd build up the query like this:
 
var p1 = args[0] as SerializedExpression;  // query
var exp = p1.ToExpression();
var baseQuery = (IQueryable)TypeFns.ConstructGenericInstance(typeof(EntityQuery<>), typeof(Product));
var query = (EntityQuery)TypeFns.ConstructGenericInstance(typeof(EntityQuery<>),
  new Type[] { baseQuery.ElementType }, exp, baseQuery);
 
 
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 30-Dec-2011 at 11:43am
Hi Kim,

Thanks for the response - this looks like it will work great. In my repository, my search method takes an ISortSelector but ToLambdaExpression is a method on the class not an extension. When I change to use SortSelector rather than ISortSelector, I run into errors where I am using CompositeSortSelector.

What would you recommend I do?

Thanks!
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 30-Dec-2011 at 12:25pm
Hmm, nothing is ever easy :).  I don't see any way to generate a single expression from the CompositeSortSelector, but it has a Selectors collection property which you can iterate through to first generate a LambdaExpression for each selector, and then a SerializedExpression for each, and then pass these along to the server method.  For example: 
 
  var sortexps = cs.Selectors.Select(s => s.ToLambdaExpression()).Select(l => SerializedExpression.ToSerializedExpression(l)).ToList();
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 30-Dec-2011 at 2:47pm
Haha. This is some pretty code, but it works.

Client:
            var sortExpressions = new List<SerializedExpression>();
 
            if (sortSelector is SortSelector)
            {
                sortExpressions.Add(SerializedExpression.ToSerializedExpression(((SortSelector)sortSelector).ToLambdaExpression()));
            }
            else
            {
                sortExpressions.AddRange(((CompositeSortSelector)sortSelector).Selectors.Select(s => SerializedExpression.ToSerializedExpression(s.ToLambdaExpression())));
            }


Server:

var expressions = sortExpressions.Select(s => s.ToExpression() as Expression<Func<Dealerstring>>);
baseQuery = baseQuery.OrderBy(expressions.First());
baseQuery = expressions.Skip(1)
                    .Aggregate(baseQuery, (current, sortExpression) => current.ThenBy(sortExpression));

Thanks for the help :)

P.S. I just took a look at the SerializedExpression code. That must have been fun to write and debug!!!



Edited by smi-mark - 30-Dec-2011 at 2:53pm
Back to Top
smi-mark View Drop Down
DevForce MVP
DevForce MVP
Avatar

Joined: 24-Feb-2009
Location: Dallas, Texas
Posts: 343
Post Options Post Options   Quote smi-mark Quote  Post ReplyReply Direct Link To This Post Posted: 03-Jan-2012 at 1:26pm
This may just be an issue with the SerializedExpression, but I have an expression like so:

var year = 2011;

Expression<Func<Statement, bool>> filter = s => s.Year == year;

This fails because it uses the variable as a reference, and not value, so when it's serialized it fails on the server. This works fine on a query, as I imagine it simply replaces it with the value.

For now I'm having to pass the parameter to the server separately.

Are there any plans for a future version to make some of this easier? If not, can I add a feature request to do so?

Thanks!
Back to Top
kimj View Drop Down
IdeaBlade
IdeaBlade
Avatar

Joined: 09-May-2007
Posts: 1391
Post Options Post Options   Quote kimj Quote  Post ReplyReply Direct Link To This Post Posted: 03-Jan-2012 at 1:58pm
We do have special logic to "constantize" local variables when passed in a query, but this is provided in one of the internal expression visitors and not available to applications.  So, your best option is probably to pass the parameter like you're doing.
 
We do have an open feature request to "provide an easier way to pass a query to an RPC method", but since you're only the second person to ask about it, it hasn't gotten a lot of attention.   I'll add your requirements to the request, and we'll see what we can do!
Back to Top
 Post Reply Post Reply

Forum Jump Forum Permissions View Drop Down