Handle Extension methods

24 views
Skip to first unread message

Jordi Cabré

unread,
May 2, 2016, 5:28:33 AM5/2/16
to re-motion Users
How's it going on?

Im really fond of re-linq. I've been able to get some handy thingies from I kicked off with them up to now.

Now, my situation is the next:

I've built an Linq provider in order to get information from our server (from now on, OL).
Ok, it works well.

Nevertheless, my boss wants to keep our client application is able to be backend agnostic. So, IoC injects a backend implementation according to some configuration values.
Shortly, our backend interface looks like:

   public interface IBackend
   
{
       
IQueryable<T> Query<T>();
       
IQueryable<Backend.Domain.C1> cs1 { get; }
       
IQueryable<Backend.Domain.C2> cs2 { get; }
       
IQueryable<Backend.Domain.C3> cs3 { get; }
       
//...
   
}

Currently, we've built three implementations: Memory, Local, and Server.

So, each one is using a linq provider:

  • MemoryBackend -> Linq Collections Provider.
  • LocalBackend -> Hibernate to Linq Provider
  • ServerBackend -> OL (our implementation using re-linq).
MemoryBackend:

        //...
       
public override IQueryable<C> cs
       
{
           
get
           
{
               
return this.csCollection.AsQueryable();
           
}
       
}
       
//....

LocalBackend:

        //...
       
public override IQueryable<C> cs
       
{
           
get
           
{
               
return this.session.Query<C>();
           
}
       
}
       
//...

ServerBackend:

        public override IQueryable<C> cs
       
{
           
get
           
{
               
return Linq.LivingQueryFactory.CreateLinqQuery<C>(this);
           
}
       
}


And all of them are using the same class model.

So, everything is working fine.

From now on, we want to extend Linq with some extension methods like:

  • Field(string field)
  • WhichEverField()
I.e. behind of WhichEverField() method is avoid to write Linq sentences like:

    this.backend.Query<C>.Where(c => c.p1.Equals("v") || c.p2.Equals("v") || c.p3.Equals("v") ...);

and write something like:

    this.backend.Query<C>.Where(c => c.WhichEverField().Equals("v"));

Or by Field() method:

    this.backend.Query<C>.Where(c => c.Field("p1").Equals("v"));

We are ready to achieve that using our Linq Provider. So, we've built some expressions which support this kind of schema pointers. However, the other Linq providers doesn't understand our custom expressions. So, we need to express the same thing using an intermediate language. This intermediate language would be to create extension methods which builds Expression tree structures.

For example, when we want to access to a field usually Linq Expression Tree is using a MemberExpression. So,

this.backend.cs.Where(c => di.Matter.Equals("v"));

BodyClause's QueryModel generated is:

    .Call ((.Extension<Remotion.Linq.Clauses.Expressions.QuerySourceReferenceExpression>).Matter).EndsWith("")

    CallExpression (MemberExpression (QuerySourceExpression, Matter member), EndsWith, "").

So, Field() extension method would have to looks like:

        public static Expression Field<T>(this object entity, string field)
       
{
           
Type entityType = typeof(T);
           
PropertyInfo propertyInfo = entityType.GetProperty(field);
           
if (propertyInfo == null)
               
throw new ArgumentException(string.Format("{0} doesn't exist on {1}", field, entityType.Name));


           
ParameterExpression parameterExpression = Expression.Parameter(entityType, "e");
           
MemberExpression memberExpression = Expression.Property(parameterExpression, propertyInfo);

           
return memberExpression;
       
}

We've suddently come up BodyClause's QueryModel generated is like:
    .Call (
       
.Call ( Backend.Infrastructure.Linq.LinqTreeExtensions.Field (
           
.Extension<Remotion.Linq.Clauses.Expressions.QuerySourceReferenceExpression>,
           
"Matter"
       
))
       
Equals("")
   
)

As you can see, QueryModel contains two calls, the call to Equals(), and another call to Field(). So, we want instead of having a Call expression to Field() method there is a MemberExpression

I hope it's clear enought.
Any ideas?
Message has been deleted

Jordi Cabré

unread,
May 2, 2016, 10:34:13 AM5/2/16
to re-motion Users
I've been digging a bit.
I've asked for some approach in a StackOverflow post. I don't know what do you think about first answer.

I've already seen some other chances to get it:
  • Lucene.Linq provides an AnyField() extension method, and handles it with a visitor. However, Lucene.Linq only provide a solution for itselft.
  • NHibernate Linq Provider allows to support this by extending a BaseHqlGeneratorForMethod class.
As far I've been able to figure out, the best option would be:
  1. To provide a default implementation for Linq Objects Provider.
  2. According to each already Linq provider implemented (NHinerbate, Entity Framework), I need to use its external mechanisms in order to replace this.
  3. If I'm implementing a Linq Provider, I'm been able to provide support for whichever extension method.
As I suggested, my first approach was to provide general intermediate language in order for several Linq providers to be able to translate this intermediate language.

Am I wrong?
Which approach would you choose?
Are they correct?
Reply all
Reply to author
Forward
0 new messages