IQueryable
When we build an expression tree over an IQueryable object , the expression itself won't be executed until we really want to get the data.
For example if we write the following code:
(DataSource is an IQueryable
var result=DataSource.Where(post=>post.Title.IndexOf("Linq")>0);
result is yet another IQueryable
result=result.Where(post=>post.CreationDate>new DateTime(2009,1,1));
Now if we've added all we wanted to our query we can execute it either by converting the result to an ICollection (a List or an array for example) or by executing GetEnumrator method ( usually by using a for each loop).
Based on this introduction I designed a custom query builder that facilitate querying an object using collected search arguments (in a windows /web form for example)
Here's the scenario that I had in mind :
There's a web/win form that lets a user fill-in some information about what he wants to see.
Consider a blog application , a user can search post based on their titles and/or their creation date.Therefore a QueryPostArgs object is created and populated using the information provided by user.
Then this object is passed to a QueryBuilder to build a query from it.Query builder contains some register query methods and when is asked to build the query it will use them to build the final query.
Enough talking let write some code :
To collect and transfer the arguments information we have an AbstractQueryArgs class.
public abstract class AbstractQueryArgs<t> where T :class
{
}
To build queries over a post object we then derived from it and create a PostQueryArgs class.
public class PostQueryArgs : AbstractQueryArgs<post>
{
public PostQueryArgs()
{
CreationDateFrom = DateTime.MinValue;
CreationDateTo = DateTime.MaxValue;
}
public string Title { get; set; }
public DateTime CreationDateFrom { get; set; }
public DateTime CreationDateTo { get; set; }
public string[] Tags { get; set; }
}
To build query using an AbstractQueryArgs we have an AbstractQueryBuilder like this:
public abstract class AbstractQueryBuilder<t,tsearchargs>
where T :class where TSearchArgs :AbstractQueryArgs<t>
{
protected delegate IQueryable<t>
QueryMethodDelegate
(IQueryable<t> queryable,TSearchArgs searchArgs);
public IQueryable<t> DataSource { get; private set; }
private readonly List<QueryMethodDelegate>
_queryMethodDelegates = new List<QueryMethodDelegate>();
protected AbstractQueryBuilder(IQueryable<t> dataSource)
{
DataSource = dataSource;
}
protected void AddQueryMethod(QueryMethodDelegate queryMethodDelegate)
{
_queryMethodDelegates.Add(queryMethodDelegate);
}
public IQueryable<t> Build(TSearchArgs searchArgs)
{
var queryable = DataSource
foreach (var queryMethodDelegate in _queryMethodDelegates)
{
queryable=queryMethodDelegate(queryable,searchArgs);
}
return queryable;
}
}
It allow us to build custom query builders for any T class.As you can see it gets an
Finally there's this Build method that build an IQueryable object using the provided datasource and passing it to each query method.As you can see this method is also returning an IQueryable
Now we can create our custom query builder for our post object as follows:
public class PostQueryBuilder:AbstractQueryBuilder<post,postqueryargs>
{
public PostQueryBuilder(IQueryable<post> dataSource) : base(dataSource)
{
AddQueryMethod(AddTitleExpression);
AddQueryMethod(AddCreationDateExpression);
AddQueryMethod(ContainsAllTags);
}
private static IQueryable<post>
ContainsAllTags(IQueryable<post> queryable , PostQueryArgs queryArgs)
{
if (queryArgs.Tags == null || queryArgs.Tags.Length == 0) return queryable;
return queryable.Where
(x => x.Tags.Intersect(queryArgs.Tags).Count() == queryArgs.Tags.Length);
}
private static IQueryable<post>
AddTitleExpression(IQueryable<post> queryable,PostQueryArgs queryArgs)
{
return string.IsNullOrEmpty(queryArgs.Title) ? queryable :
queryable.Where(x=>x.Title.IndexOf(queryArgs.Title)!=0);
}
private static IQueryable<post>
AddCreationDateExpression(IQueryable<post> queryable,
PostQueryArgs queryArgs)
{
return queryable.Where(
x => x.CreationDate >= queryArgs.CreationDateFrom &&
x.CreationDate <= queryArgs.CreationDateTo); }
As you can see this class add three query methods to query methods list.One for title,one for creation date and one for tags.
Now that we have created these classes , all we need to do to build a query on a post datasource is like this:
var posts=new []{_post1,_post2,_post3};
var queryBuilder=new PostQueryBuilder(posts);
var queryArgs=new PostQueryArgs{Title="Intro",Tags=new []{"C#","Linq"}};
var query=queryBuilder.Buid(queryArgs);
Then we have a IQeryable
P.S. I have upload the source code in codeproject site you can fint it here.
1 comment:
Hi
Its great if you can share in codegain.com also.
Thank you
Post a Comment