Create a custom ResultOperator

23 views
Skip to first unread message

Jordi Cabré

unread,
Sep 21, 2016, 7:20:28 AM9/21/16
to re-motion Users
I've create an extension method SingleOrEmpty:

public static TSource SingleOrEmpty<TSource>(this IEnumerable<TSource> source)
{
   
if (source.Any())
       
return source.SingleOrDefault();
   
else
       
return Activator.CreateInstance<TSource>();
}

As you can see I'm trying to return an instance of TSource using a single constructor.

I would like to use tihs method but I don't know how to declare it as a ResultOperator.

Is it possible?

Fabian Schmied

unread,
Sep 21, 2016, 3:25:47 PM9/21/16
to re-moti...@googlegroups.com
It might not be 100% up to date, but the basic idea should still work.

Best regards,
Fabian

--
You received this message because you are subscribed to the Google Groups "re-motion Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to re-motion-users+unsubscribe@googlegroups.com.
To post to this group, send email to re-motion-users@googlegroups.com.
Visit this group at https://groups.google.com/group/re-motion-users.
For more options, visit https://groups.google.com/d/optout.

Jordi Cabré

unread,
Sep 22, 2016, 7:49:05 AM9/22/16
to re-motion Users

It's been really usefull this documentation. Thanks and great job.

As far I've been able to figure out, I've created a ResultOperator (: SequenceTypePreservingResultOperatorBase) and a re-linq hook (: ResultOperatorExpressionNodeBase) and finally register it on re-linq.

I've created my custom Linq provider using re-linq in order to get information from a server. So, I provide some util methods (MetaInfoKey bellow) in order to create Expressions in order to avoid work to the user. For example:

this.backend.Query<Backend.Domain.FollowUpActivity>()
   
.GroupBy(LinqTreeExtensions.Query<Backend.Domain.FollowUpActivity>(f => f).MetaInfoKey<Backend.Domain.FollowUpActivity, string>("Key"))
   
.Select(g => new { Value = g.Key, Count = g.Count() });

I need these custom made expressions are `compatible` with "Linq over collections" provider.
Shortly, the expression is generating MetaInfoKey is: {f => Convert(f.Metainfos.Where(m => (m.Key == "Key")).SingleOrEmpty().Value)}

In order to build it I do:

ParameterExpression entityParameter = expr.Parameters.First();
ParameterExpression metaInfoParameterExpression = Expression.Parameter(collGenericType, "m");

MemberExpression collectionMemberExpression = Expression.Property(entityParameter, collectionPropertyInfo);
MethodInfo whereMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("Where") && m.GetParameters().Length == 2).First().MakeGenericMethod(collGenericType);
MethodInfo singleMethod = typeof(LinqTreeExtensions).GetMethods().Where(m => m.Name.Equals("SingleOrEmpty") && m.GetParameters().Length == 1).First().MakeGenericMethod(collGenericType);

LambdaExpression innerCondition = Expression.Lambda(
   
Expression.GetDelegateType(collGenericType, typeof(bool)),
   
Expression.Equal(
       
Expression.Property(metaInfoParameterExpression, "Key"),
       
Expression.Constant(field)
   
),
    metaInfoParameterExpression
);

MethodCallExpression whereCallExpression = Expression.Call(whereMethod, collectionMemberExpression, innerCondition);
MethodCallExpression singleCallExpression = Expression.Call(singleMethod, whereCallExpression);
MemberExpression memberExpression = Expression.Property(singleCallExpression, "Value");
UnaryExpression convertExpression = Expression.Convert(memberExpression, typeof(TKeyType));
Expression<Func<TElementType, TKeyType>> lambdaExpression = Expression.Lambda<Func<TElementType, TKeyType>>(convertExpression, entityParameter);

return lambdaExpression;

The first problem I'm facing up is how to implement the
SingleOrEmpty extension method:

public static IQueryable<TSource> SingleOrEmpty<TSource>(this IQueryable<TSource> source)
{
   
return source.Provider.CreateQuery<TSource>(
       
Expression.Call(
           
((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource)),
            source
.Expression
       
)
   
);
}

I'm guessing it's building a CallMethodExpression, but where's the implementation? Remember that it's to be performed over Linq-Collection as well...

I hope I've explained so well.





Fabian Schmied

unread,
Sep 24, 2016, 3:29:33 AM9/24/16
to re-moti...@googlegroups.com
> I'm guessing it's building a CallMethodExpression, but where's the
> implementation? Remember that it's to be performed over
> Linq-Collection as well...

Take a look at SingleOrDefault as a template for your implementation. There are two SingleOrDefault methods; one is defined on IEnumerable, the other on IQueryable. The IEnumerable one has a "real" implementation; it's meant to be used with normal enumerable sequences and collections. The IQueryable one has the "meta" implementation you've already written for your own method; it's meant to be used when constructing queries to be translated by a LINQ provider.

With re-linq, you usually handle both variants because even the method for IEnumerable can be used within a query, for subqueries.

Best regards,
Fabian

Reply all
Reply to author
Forward
0 new messages