Tuesday, November 16, 2010

Converting Lambda expressions at runtime

DevExpress XPO as all modern ORM supports object querying using linq and lamda expressions. A sample query can be written as

            var xpQuery = new XPQuery<Customer>(Session.DefaultSession);

            IQueryable<Customer> customers = xpQuery.Where(customer => customer.Age == 25);

 

From the above query we can get a list of customers that are 25  years old . As you can see the code is very simple.

The problem 
When working with modularized applications you may be in context that you have access to the Customer type  only at runtime or you may want to apply that query to different object types that all have an Age property. How that can be done?

1st attempt

Lets register the customer to our module and use reflection to create the XPQuery.

            ModuleXXX moduleXxx = new ModuleXXX();

            moduleXxx.DoQuery(typeof (Customer));

 

and the DoQuery method will be something like

 

        public void DoQuery(Type customerType) {

            Type makeGenericType = typeof (XPQuery<>).MakeGenericType(customerType);

            object xpQuery = Activator.CreateInstance(makeGenericType, new object[] {Session.DefaultSession});

            //THIS IS A DEAD END

            //although we can construct the XPQuery object we have to stop here cause there is no way to use it since we not have the type at design time

        }

 2nd Attempt

Refactor our customer using an ISupportAge interface that will live in our module and use that interface with the DoQuery method

    public interface ISupportAge {

        int Age { get; set; }

    }

 

    public class Customer : BaseObject, ISupportAge {

        public Customer(Session session) : base(session) {

        }

        private int _age;

        public int Age {

            get {

                return _age;

            }

            set {

                SetPropertyValue("Age", ref _age, value);

            }

        }

    }

 

Now the DoQuery will have no arguments

 

            ModuleXXX moduleXxx = new ModuleXXX();

            moduleXxx.DoQuery();

 

and will look like

 

        public IQueryable<ISupportAge> DoQuery() {

            var xpQuery = new XPQuery<ISupportAge>(Session.DefaultSession);

            return xpQuery.Where(customer => customer.Age == 25);

        }

 

as you see in the code above in order to be able to use lamda expressions i have passed the ISupportAge interface in my XPQuery method. Too bad that XPQuery supports as generic parameters only persistent types and not interfaces

 

3rd Attempt

The previous attempt was pretty closed to what we wanted. Brainstorming and researching a little more we can find that lambda expressions are fully decomposable. That means that they consist of parts like body,parameters,sub expressions that can be decomposed and recomposed again. After decomposing the tree using Xpand.Utils.Linq.ExpressionConverter the DoQuery method now will  look similar to

 

        public IQueryable<ISupportAge> DoQuery(Type customerType) {

            //here we create a lambda based on the ISupportAge interface

            Expression<Func<ISupportAge, bool>> expression = date => date.Age == 25;

 

            //Convert the lambda using the customerType argument the new expression will be of type Expression<Func<Customer, bool>>

            //but since our method knows nothing about customer we have used the abstract Expression type.

            Expression convert = new ExpressionConverter().Convert(customerType, expression);

 

            //create the Xpquery instance

            Type xpQueryGenericType = typeof(XPQuery<>).MakeGenericType(new[] { customerType });

            object xpquery = Activator.CreateInstance(xpQueryGenericType, new[] { Session.DefaultSession });

 

            //find the where method

            MethodInfo mi = typeof(Queryable).GetMethods().Where(info => info.Name=="Where").FirstOrDefault();

            mi = mi.MakeGenericMethod(customerType);

 

            //execute the where but now using the converted lambda

            return ((IEnumerable)mi.Invoke(xpquery, new[] { xpquery, convert })).OfType<ISupportAge>().AsQueryable();

        }

 

You can find the ExpressionConverter at eXpand source code avalibale at http://expandframework.com/download.html

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

Wednesday, November 3, 2010

DevExpress interviewed eXpand framework

Rachel Hawley from DevExpress interviewed me over expand some days ago. You can read more at her blog

eXpand: An open source framework built with XAF

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

Monday, November 1, 2010

Runtime Orphaned Collections

eXpand already provided support for various runtime member scenarios such as

Calculated properties at runtime—TIMES 4
Dynamic Types with WorldCreator source code is out

In the post I am going to speak about our latest addition which is support for runtime orphaned collections.

Using Model Editor

As you see in the image bellow you only have to create a new IModelRuntimeOrphanedColection image

set CollectionType attribute (1)

image

and filter the collection by using the criteria (2).

Although the Model Editor approach with a combination of the ModelDifference module can provide great flexibility such as defining Runtime Collections for a set of Roles or Users has a drawback. It does not add those collections in any of your views. You have to do that manually.

Using WorldCreator Extended Classes

This approach overcomes the previous Model Editor drawback, since the runtime collections are created before the model. Hence your runtime collections are added to all related views

 image

As you saw in the above images that functionality is demoed at eXpand feature center http://expandframework.com/download.html

Subscribe to XAF feed
Subscribe to community feed

DiggIt!