Query conditions

Support for "<", ">" , "<=", ">=" along with "=" and != Expression

protected override void Process(LinqExtender.Interface.IModify<T> items, Bucket bucket)
{
  // the following line with throw a "not supported by the provider" exception
  // if multiple where clause for same item is used in this case "id"
  object obj = bucket.Items["Id"].Value;
  // if provider author wants to support multiple where clause queries like
  // where book.Id > 1 && book.Id < 3 , they can take the advantage of the following
  IList<BucketItem.QueryCondition> conditions = bucket.Items["Id"].Values
  foreach (BucketItem.QueryCondition condition in conditions)
  {
    // do whatever necessary, but properties are
    obj value = condition.Value;
    RelationType relationType = condition.RelationType;
  }
}




In memory Sort for orderby query

protected override void Process(LinqExtender.Interface.IModify<T> items, Bucket bucket)
{
   // do all work
   // get the result
   
   // finally intead of
   items.AddRange(IEnumerable<T>);
   //Use
   items.AddRange(IEnumerable<T>, true); 
   // which does a in-memory order by if "order by" clause is used in query.
}



Simalary, for single item fill up, lets say you take an item from the source , do some work and add it to the collection one by one , then items.Sort can be used.

protected override void Process(LinqExtender.Interface.IModify<T> items, Bucket bucket)
{

   foreach (T item in result)
   {
     // do some work 
     items.Add(T);
   }
   // the following line will do a sort on the orderby query.	
   items.Sort(); 
}


Sort is useful for creating serviced api. For example. for flickr i cant pass orderyby for popular tags.
In this case the in-memory sort will be used.

Natural Sort

1 You can handle sorting by yourself for orderby Ascending / descending. Inside Process method

if (bucket.OrderByCaluse != null)
{
   // there are two items to check for 
   1. bucket.OrderByCaluse.FieldName , the item name or constant on which order by is made, 
      if OriginalNameAttribte  is used on a Property , 
      then the name will be taken from it or it will be the property name.
   2.  bucket.OrderByCaluse.IsAscending = true/ false 
}




Sync back reference

If any BucketItem is changed inside Add methoed of implmented Query<T> of Bucket.Items collection then it will reflected back to the referencing object.
This is useful when we need to track back some property (lets say Id) after adding the object to the datasource.

Ex
Photo photo  = new Photo();
photo.Title = "Test";
Assert.IsTrue(photo.Id == 0);
context.Add(photo);
Assert.IsTrue(photo.Id > 0);



Complex parameter

For example, we can do the following

var query = from q in _queryContext
where q.Author == _list[2].Author // complex type query parameter.
select q;



Query.Count

var query = (from q in _queryContext
             orderby q.LastUpdated descending
             select new { q.Id, q.Title, q.LastUpdated }).Take(1);

int count = query.Count(); 
	




Projection

 var query = (from q in context
              orderby q.LastUpdated descending
              select new { q.Id, q.Title, q.LastUpdated }).Take(1);



Logical expressions

This are tracked by RelationType enum, and is passed with Bucket through BucketItem.QueryCondition to Process method exposed by LinqExtender,
where user can take logical decision based on the Relation Type.

Typical expression

var query = from q in context
                  where q.Age < 23
                  select q;


for this, inside the bucket wrapper, passed in the Process Routine, we can retrive the value in the followng way.

int value = (int) bucket.Items["Age"].Value ;
RelationType relType = bucket.Items["Age"].RelationType;

Here , the assinged value for value and relType are 23 and RelationType.LessThan respectively.


Order by support


In query support for orderby Ascending / descending. Inside Process method , we can do the following.


if (bucket.OrderByCaluse != null)
{
   // there are two items to check for 
   1. bucket.OrderByCaluse.FieldName , the item name or constant on which order by is made, 
      if OriginalNameAttribte  is used on a Property , 
      then the name will be taken from it or it will be the property name.
   2.  bucket.OrderByCaluse.IsAscending = true/ false 
}




Singular method calls

Take, Skip , First , Single , Last method calls are possible to get specific object.

  var query = (from q in _queryContext
  select q).Take(1).Skip(2);

  Book book  = query.Single()/ First();


Object tracking service

Book book = new Book { Title = "Programming LINQ", Author = "Pablo Pilarosi" }

context.Add(book);

context.SubmitChanges();

This will raise an Additem(Bucket item) call in Query<T> class where you can write your logic for adding
the new object to database or serviced destination.

protected bool AddItem (Bucket Item)
{
    //once you have added the item to soruce and got the Identity Id
    // you can do
    bucket.Items["Id"] = 123; // this will update the main object.
    // true means success, false will raise an appropiate OnError event
    return true / false;
}


Similarly.
var query = (from q in context
  select q).Take(1).Skip(2);
Book book  = query.Single();

context.Remove(book);
context.SubmitChanges();



This will cause the call of Query<T>.RemoveItem(Bucket) : bool

Inside it write your appropiate logic for removeing the object from source , On successfull delete the object will be
removed from the context collection.

For updating , you would need to do the following

Book book = (from book in context.Books
where book.Id = 1 ).Single();

// now update a property
book.ISBN = "110";
//then call
context.SubmitChanges();

LinqExtender knows the object is updated and thus will call Query<T>.UpdateItem(Bucket). So, in your provider to support
object update you need to override the following
protected bool UpdateItem(Bucket item)
{ 
    Bucket contains the updated property- value map
   
    //finally do a return , if true, then only the
    //collection will be updated or 
    //it will revert back to the old one and raise an OnError event.
    return true/false;   
}



Getting of identity Item
protected override T GetItem(Bucket item)
{
   T singleObject = GetItThoughUniqueValue(...);
   return singleObject;
}
 


This will be called by the toolkit for queries like

var query = from book in context.Books
where book.Id == 1
select book

Book book = query.Single();

Here, Id is a unique field defined by UniqueIdentifier attribute. This saves few if-elses in Query<T>.Process. when getting single unique item. If you are writting a provider that auto generates the query depending on Bucket properties, you can do that well with Quey<T>.Process and in that case you dont need override this for Identity calls, but if your building a serviced API like LINQToFlickr and you want your GetById and Search in differnent scope rather using IF-ELSE-THEN in Query<T>.Process , then it will be useful.

Maping Attribute
OriginalEnityName attribute for class like the OrignalFieldName for propeties. If you have table name other than class name You can use this attribute to map it with original table name, similar thing goes for properties.

Others

Bucket.QueryVisible , true [if, LinqVisible(true) added on a property, it can be used in a query] , false
[If, LinqVisible(false)] , LinqVisibleAttribute was previously known as UseInQueryAttribute.

IDisposable for QueryObjectBase, in this way provider can be used in using (..) { } block and provider writers can write appropiate clean up code.


OnSuccess and OnError event handler included. This is for , people using custom provider made on Linqextender , can take the
advantage of it. For example, writing logs.

OnError is raised for any exception in Process, Add , Remove overrides.
OnSuccess is raised, after doing db.SubmitChanges(), for successful data action.

UniqueIdentifierAttribute - This will let you mark a field or property Unique for delete or update like stuff.

Overview of Bucket object

Bucket
-IDictionary<string, BucketItem> Items - > where string is the property name of the query object, BuckItem represents how the Property is treated and used in query.
-ItemsToTake : default null , if Take is not specified in query.
- ItemsToTake : default 0, if Skip is not specified in query.
-UniqueItems : string[], contains the name of property on which UniqueIdentifierAttribute or its derivative is used.

BucketItem
- Name : Different if the used With OriginalFieldNameAttribute, or less it is same as property name
- Value : value in the query , for which the property is compared.
- Unique : True , if UniqueIdentifierAttribute or its inherited type is used on Property, Here one can Use UniqueIdentifierAttribute directly on property or can any inherited like PrimaryKeyAttribute that inherites the UniqueIdent
- QueryCondition: Holds the different query conditions for logical comparision in where clause.
- Values : BucketItem.QueryCondition , holds Value and RelationType for multiple where caluse per field

Last edited Aug 10, 2008 at 8:33 PM by mehfuzh, version 10

Comments

No comments yet.