Wednesday, May 29, 2013

Sharepoint 2010 Querying Sharepoint Data

There are many ways we can query sharepoint data. Below are the objects used for querying sharepoint data.

1) SPQuery - This is used to retrieve data from a specific list.

2) SPSiteDataQuery - This is used to query data from multiple lists across different sites in a site collection.

3) CrossListQueryCache - The SharePoint CrossListQueryCache class provides a highly scalable way to run site wide queries.  It provides all the power of SPSiteDataQuery without the performance implications.  However it has some major gotchas that you should be aware of before using.

4) PortalSiteMapProvider - The PortalSiteMapProvider class allows us to access cached objects for sites, lists, and list items.

Details and Examples:

SPQuery:

Scenario:

To get the list of items from SharePoint list whose age is greater than 10.
Assumption: Student List is created with FirstName, LastName, Age and the name of the list is Student.

Code:

SPWeb spWeb = SPContext.Current.Site.RootWeb;
SPList spList = spWeb.Lists["Students"];
SPQuery spQuery = new SPQuery();
spQuery.Query = "Write Caml query here"  //like  select * from table where Age > 10

//Execution of below line of code will return SPListItemCollection

SPListItemCollection collListItems = spList.GetItems(spQuery);
 
//Loop through each item and perform the operation

foreach (SPListItem spListItem in collListItems) {
         //Process each item
}

There will be an question arise why can't we use Linq instead of SPQuery.

If we have Linq then it needs to run under full trust proxy which is not possible in sand box solution at that time we need to use SPQuery.


SPSiteDataQuery:

Scenario:

To get list of items from different list under multiple sites(webs) under same site collection.

Follow these steps for using the SPSiteDataQuery class
-          Create a SPSiteDataQuery instance.
-          Set properties on the SPSiteDataQuery instance to specify the lists or list types to include in the query, the individual sites to include in the query, and the CAML query itself.
-          Call the GetSiteData method on a SPWeb instance, passing in the SPSiteDataQuery instance as a parameter. The GetSiteData method returns a DataTable.

Code:

       SPSiteDataQuery dataQuery = new SPSiteDataQuery();
      //Here if we set Scope to Recursive then the query considers the current Web site and  all subsites of the current Web site.

     //If we set Scope to SiteCollection, the query considers all Web sites that are in the same site collection as the current Web site.
    //Code snippet will be like dataQuery.Webs = "";
      dataQuery.Webs = "";
      dataQuery.Lists = "";
      dataQuery.ViewFields = "";
      dataQuery.Query =  "12345
"; 
      DataTable dtable = web.GetSiteData(dataQuery);
      //Do whatever the required operations here
 
When to use SPSiteDataQuery:
 
SPSiteDataQuery is commonly used in list aggregation scenarios, where list data from team sites or other subsites is collated and presented in a single interface. The SPSiteDataQuery will only aggregate data from SharePoint lists and will ignore data from external lists.
In terms of efficiency, the SPSiteDataQuery class provides an optimal approach to data access in the following scenarios:
-          When we need to query multiple lists within the same site collection for the same content
-          When we need to query across two or more lists that are not related by lookup columns
The SPSiteDataQuery class is optimized for querying list data across multiple sites in a site collection and across multiple lists within a single site.

CrossListQueryCache:

The SharePoint CrossListQueryCache class provides a highly scalable way to run site wide queries.
The CrossListQueryInfo object uses the CrossListQueryInfo object to get the cached results or, if there are no cached results available, it performs a cross-list query to the database and then caches the results for future use.

How to use CrossListQueryCache:
 CrossListQueryInfo query = new CrossListQueryInfo();
 query.Lists = "";
  query.Query = "" +
                          "Completed";
   query.ViewFields = "";
      query.UseCache = true;
 CrossListQueryCache cache = new CrossListQueryCache(query);
 DataTable results = cache.GetSiteData(spSite);

Where to use CrossListQueryCache:

It should be used when we need to query one or more lists and store the results in cache so subsequent calls are at near lightning speed.
There are 4 overloads for CrossListQueryCache.GetSiteData.  Two of the overloads cache the results and two do not.

CrossListQueryCache RoutineUses Cache
GetSiteData (SPSite)Yes
GetSiteData (SPWeb)No
GetSiteData (SPSite, String)Yes
GetSiteData (SPWeb, SPSiteDataQuery)No
 
So unless we are using one of the overloads that support caching and have the CrossListQueryInfo.UseCache property set to true we might as well use SPSiteDataQuery.
 
PortalSiteMapProvider:

The PortalSiteMapProvider class allows us to access cached objects for sites, lists, and list items. It also provides methods for executing SPQuery and SPSiteDataQuery queries against cached SharePoint lists. We can use the GetCachedListItemsByQuery method to cache the query results. Executing a SPSiteDataQuery query with a PortalSiteMapProvider object is similar to using the CrossListQueryCache class.

How to use PortalSiteMapProvider:

To retrieve items from a list PortalSiteMapProvider class includes a method called GetCachedListItemsByQuery that retrieves data from a list based on a SPQuery object that is provided as a parameter to the method call.
The method then looks in its cache to see if the items already exist. If they do, the method returns the cached results, and if not, it queries the list, stores the results in cache and returns them from the method call.

Code:

SPQuery spQuery = new SPQuery();
spQuery.Query = ”” +
Completed
”;PortalSiteMapProvider ps = PortalSiteMapProvider.WebSiteMapProvider;
var pNode = ps.FindSiteMapNode(web.ServerRelativeUrl) as PortalWebSiteMapNode;

var Items = ps.GetCachedListItemsByQuery(pNode, ”Tasks”, spQuery, web);
foreach (var item in Items )
{
//Do what ever want here
}

Where to use PortalSiteMapProvider:

A primary advantage of the PortalMapSiteProvider class is its use and management of the SharePoint object cache. The object cache provides highly efficient access to objects that are not part of a list. By default, the PortalSiteMapProvider class queries and manages the object cache.

Thursday, May 16, 2013

Sharepoint 2010 Timer Jobs

In this article I am going to show how to make our timer job to run in the specified servers in the farm which is having Front end server or App server.

If we don't specify server then timer jobs will run in any of the front/app servers.

We have a requirement where we need to run our timer job only in app server since our timer job was making an web service call. You may think what's the wrong running timer job in front end server. Here ports(where we are making call to web service) were opened only in app servers not in FE servers.

Suppose if the timer job picks from front server then definitely it will throw exception in order to avoid this situation we change the logic to run only in app servers as shown below, before that I will give an overview of sharepoint timer jobs.

Sharepoint timer jobs should inherit from the SPJobDefinition class. This class provides 3 constructors for us:

SPJobDefinition()Default constructor needed for serialization purposes. Initializes a new instance of the SPJobDefinition class.
SPJobDefinition(String, SPWebApplication, SPServer, SPJobLockType) Instantiates a timer job associated with the given SPWebApplication. Initializes a new instance of the SPJobDefinition class and provides parameters for specifying key objects.
SPJobDefinition(String, SPService, SPServer, SPJobLockType) Instantiates a timer job associated with the given SPService. Initializes a new instance of the SPJobDefinition class and provides parameters for specifying key objects.

First constructor is required in any of the custom timer job and we can use either 2nd or 3rd constructor in our code but either one of them is mandatory(Job definitions must be associated with a service or Web application)

Sample code:

public class Sharepoint2010TimerJob : SPJobDefinition
{
     public Sharepoint2010TimerJob(): base()
     {
     }

     public Sharepoint2010TimerJob(string name, SPService service, SPServer server,      SPJobLockType lockType) : base(name, service, server, lockType)
     {
     }

     public Sharepoint2010TimerJob(string name, SPWebApplication webApplication, SPServer server,      SPJobLockType lockType) : base(name, webApplication, server, lockType)
     {
     }

     public override void Execute(Guid targetInstanceId)
     {     
             //Execute Timer Job Tasks
            //This is the place where we are making our web service call and doing executing the logic
     }
}

Below is our normal sharepoint job definition code now I will show how to make our timer service to run in the specified server.

Open the feature receiver class for our timer job.

     [Guid("{39ad45db-49a6-45d2-ae99-4cd677ecf65d}")]
     public class Sharepoint2010TimerFeatureReceiver :      SPFeatureReceiver
     {
    
              string timerJobName = "SharePoint 2010 Timer job";
    
              public override void FeatureActivated(SPFeatureReceiverProperties properties)
             {
                       SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
                
                      //Delete the job if it is already existing in our case this is not required we can comment the below line.
                      DeleteJob(webApp.JobDefinitions, timerJobName);

                     //Declare the SPServer object
                     SPServer serverToRunOn = null;

                    //Get all the servers in the farm
                    SPServerCollection servers = SPFarm.Local.Servers;

                   //Checking for property bag entry if it is already there then assign the value
                    if (webApp.Properties[timerJobName + "_Server"] != null)
                   {
                              serverToRunOn = servers[webApp.Properties[timerJobName + "_Server"].ToString()];
                    }

                 //Here we can check if form is configured with single server
                  if (serverToRunOn == null)
                  {
                           foreach (SPServer server in servers)
                           {
                                   if (server.Role == SPServerRole.SingleServer)
                                  {
                                           serverToRunOn = server;
                                           break;
                                   }
                            }
                    }
              
                   // if you want to run timer service in app server then below is the code we need to use
                   if (serverToRunOn == null)
                  {
                           foreach (SPServer server in servers)
                          {
                                   if (server.Role == SPServerRole.Application)
                                  {
                                             serverToRunOn = server;
                                             break;
                                  }   
                          }
                  }

                  // If we want to run the code in front end server then use the below piece of code.
                  if (serverToRunOn == null)
                 {
                          foreach (SPServer server in servers)     
                         {
                                if (server.Role == SPServerRole.WebFrontEnd)
                               {
                                         serverToRunOn = server;
                                         break;
                               }
                        }
                 }

               //In the above piece of code I am checking so many conditions and may be every one will think why so many conditions are required if I need to assign for only app server.

             //Here is my answer lets assume we need to deploy the code in dev server where its running in single server then 1st condition will be useful

            //If we move the code to staging environment which is running with multiple server but no dedicated app server then we require 3rd condition
    
           //If we move the code to ppe(pre production environment) which is replica of Prod then it will have proper sharepoint set up like 2 front end, 1 app server , 1 db server(small farm) then 2nd condtion will execute.

          //This is what we are looking to run the timer job to run in app server.
        
          // Define the schedule
          Sharepoint2010TimerJob timerJob = new Sharepoint2010TimerJob(timerJobName, webApp, serverToRunOn, SPJobLockType.Job);

          webApp.Properties[timerJobName + "_Server"] = serverToRunOn.Name;

          //Schedule as per the requirement
          SPMinuteSchedule minuteSchedule = new SPMinuteSchedule();
          minuteSchedule.BeginSecond = 1;
          minuteSchedule.EndSecond = 59;
          minuteSchedule.Interval = 10;
          timerJob.Schedule = minuteSchedule;
          // Update the schedule      timerJob.Update();
       }
  }
}