Quantcast
Channel: eXpandFramework RSS
Viewing all articles
Browse latest Browse all 861

BLOG by Robert Anderson: Fluent queries with DevExpress XPO - Implementation

$
0
0

Continuing from my last post, I’ll demonstrate how to create a fluent interface so that you can do:

123456789101112
varcustomer=Session.Query().InTransaction.Contacts.ByPosition("Developer").ThatHave.NoPhoto().And.TasksInProgress().And.TasksWith(Priority.High).FirstOrDefault();

First, let’s look at the ‘beginning’ of the fluent interface: the Query() extension method.

123456789101112131415
publicstaticclassQueryExtensions{publicstaticIQueriesQuery(thisSessionsession){returnnewQueries(session);}// If we're using XAF, do the same for ObjectSpace as wellpublicstaticIQueriesQuery(thisIObjectSpaceobjectSpace){varxpObjectSpace=objectSpaceasXPObjectSpace;varsession=xpObjectSpace.Session;returnnewQueries(session);}}

What does the Queries() class look like?

12345678910111213141516171819202122232425262728293031323334353637383940
publicinterfaceIQueries{IQueriesInTransaction{get;}IContactQueriesContacts{get;}// One for each queryable object type, e.g.,// IDepartmentQueries Departments { get; }       // ITaskQueries Tasks { get; }// etc.}publicclassQueries:IQueries{publicQueries(Sessionsession){_Session=session;}privatereadonlySession_Session;privatebool_InTransaction;publicIQueriesInTransaction{get{_InTransaction=true;returnthis;}}privateIContactQueries_Contacts;publicIContactQueriesContacts{get{if(_Contacts==null)_Contacts=newContactQueries(_Session,_InTransaction);return_Contacts;}}}

If we ignore the InTransaction property, it is just a container for the IContactQueries. In your application, you would have a similar property for each queryable object type. A new ContactQueries instance is created on demand taking into account the whether the InTransaction property was visited earlier in the syntax.

Now, let’s look at the base classes.

12345678910111213141516171819202122232425
publicinterfaceIQueries<T>:IEnumerable<T>,IFluentInterface{}publicclassQueries<T>:IQueries<T>{publicQueries(Sessionsession,boolinTransaction){_Session=session;Query=newXPQuery<T>(session,inTransaction);}privatereadonlySession_Session;protectedIQueryable<T>Query{get;set;}publicIEnumerator<T>GetEnumerator(){returnQuery.GetEnumerator();}IEnumeratorIEnumerable.GetEnumerator(){returnQuery.GetEnumerator();}}

So Queries<T> wraps an XPQuery<T>.

Side note: the inclusion of IFluentInterface is a clever trick to improve Intellisense by hiding the System.Object members such as ToString(). See Daniel Cazzulino’s blog post.

And now we can implement the Contact generic as follows:

12345678910111213141516171819202122232425262728293031
publicinterfaceIContactQueries:IQueries<Contact>{IContactQueriesByDepartmentTitle(stringdepartmentTitle);IContactQueriesByPosition(stringposition);ContactByEmail(stringemail);}publicclassContactQueries:Queries<Contact>,IContactQueries,IContactThatHaveQueries{publicContactQueries(Sessionsession,boolinTransaction):base(session,inTransaction){}publicIContactQueriesByDepartmentTitle(stringdepartment){Query=Query.Where(p=>p.Department.Title==department);returnthis;}publicIContactQueriesByPosition(stringposition){Query=Query.Where(p=>p.Position.Title==position);returnthis;}publicContactByEmail(stringemail){returnQuery.SingleOrDefault(p=>p.Email==email);}}

There we go. Now we can use our fluent interface:

1
varcontacts=session.Query().Contacts.ByPosition("Manager");

Much more readable. Also more maintainable because all queries are in one place and make use of good old LINQ. It’s also easier to test the queries because they are independent of the calling code.

See a sample implementation built against the DevExpress XAF MainDemo on GitHub.


Viewing all articles
Browse latest Browse all 861

Trending Articles