In this particular article, we are going to can create a custom timer job in SharePoint 2016 using Visual Studio 2015.  The steps are the same for SharePoint 2013 as well. In this example, the timer job will do the following tasks. - It will read the items from a SharePoint list. - Then it will create subsites under the site by using column values from the custom list.

This is a basic example but you can always modify and add code according to the requirement.

Timer jobs are like background processes which do certain tasks and is managed by SharePoint. By default there are various timer jobs are available in SharePoint which you can view from central administration.

If you have not tried SharePoint 2016, you can check the SharePoint 2016 installation steps.

Open SharePoint 2016 Central Administration -> Monitoring -> Then under Timer Jobs click on "Review job definitions". This page will display all the default timer jobs.

Steps to Create a Custom Timer Job: Open visual studio 2016 and then File -> New -> Project...

Then select SharePoint Solution which is under Templates -> Visual C# -> Office/SharePoint. Then choose SharePoint 2016 - Empty Project. Give a name and click on OK.

Then provide a local SharePoint site for debugging and then choose to Deploy as a farm solution.

Now in the next step add a class to the project.

Right click on the Project -> Add -> New Item... Then choose a class file from Visual C# Items -> Code.

Then the class should inherit from  Microsoft.SharePoint.Administration.SPJobDefinition class.

So the class should look like below:

using System;
 
using System.Collections.Generic;
 
using System.Linq;
 
using System.Text;
 
using System.Threading.Tasks;
 
using Microsoft.SharePoint.Administration;
 
namespace CustomTimerJob
 
{
 
    Public class OurCustomTimerJob:SPJobDefinition
 
    {
 
    }
 
}

After this add three constructors to the class with few parameters and the class now should looks like below:

using System;
 
using System.Collections.Generic;
 
using System.Linq;
 
using System.Text;
 
using System.Threading.Tasks;
 
using Microsoft.SharePoint.Administration;
 
namespace CustomTimerJob
 
{
 
    Public class OurCustomTimerJob : SPJobDefinition
 
    {
 
        public OurCustomTimerJob() : base() { }
 
        public OurCustomTimerJob(string jobName, SPService service) :
 
          base(jobName, service, null, SPJobLockType.None)
 
        {
 
            this.Title = "Our Custom Timer Job";
 
        }
 
        public OurCustomTimerJob(string jobName, SPWebApplication webapp) :
 
        base(jobName, webapp, null, SPJobLockType.ContentDatabase)
 
        {
 
            this.Title = "Our Custom Timer Job";
 
        }
 
    }
 
}

Then we need to override the Execute method which will contain our business logic. Now we can add the business logic here in the Execute method.

So here I have a list which has a structure like below:

Then we have to override the Execute method where we have to keep the business logic. This is the only method where we have to add the logic.

using System;
 
using System.Collections.Generic;
 
using System.Linq;
 
using System.Text;
 
using System.Threading.Tasks;
 
using Microsoft.SharePoint.Administration;
 
namespace CustomTimerJob
 
{
 
    Public class OurCustomTimerJob : SPJobDefinition
 
    {
 
        public OurCustomTimerJob() : base() { }
 
        public OurCustomTimerJob(string jobName, SPService service) :
 
          base(jobName, service, null, SPJobLockType.None)
 
        {
 
            this.Title = "Our Custom Timer Job";
 
        }
 
        public OurCustomTimerJob(string jobName, SPWebApplication webapp) :
 
        base(jobName, webapp, null, SPJobLockType.ContentDatabase)
 
        {
 
            this.Title = "Our Custom Timer Job";
 
        }
 
    }
 
}

The above code will pick items from the list and will create a subsite with the supplied parameters.

Add Feature to the Timer Job: Then right click on the Features folder and then click on Add Feature. This will add a custom feature to it. You can rename the feature for an appropriate name. 

Give a proper Title for your feature, also you can provide a description of the feature. Then set the scope to WebApplication like below:

If you want the feature to be activated by default then you can change Activate On Default property from False to True like below:

Then right-click on the Feature and then click on Add Event Receiver. In the event receiver file, we will add logic to handle FeatureActivated and FeatureDeactivating method.

In the Feature Activated, We will first delete the existing job and then create the new job. Similarly, in the FeatureDeactivating method, we will simply delete the existing job.

Here we have scheduled the job to run in every 5 minutes. But you can set the time interval according to your requirement.

You can see: SharePoint 2016 Timer Job Schedule Options Visual Studio 2015

The full code will look like below:

using System;
 
using System.Runtime.InteropServices;
 
using System.Security.Permissions;
 
using Microsoft.SharePoint;
 
using Microsoft.SharePoint.Administration;
 
namespace CustomTimerJob.Features.OurCustomTimerJobFeature
 
{
 
    /// <summary>
 
    /// This class handles events raised during feature activation, deactivation, installation, uninstallation, and upgrade.
 
    /// </summary>
 
    /// <remarks>
 
    /// The GUID attached to this class may be used during packaging and should not be modified.
 
    /// </remarks>
 
    [Guid("f01fcc35-3b30-434a-b43a-fadc69a6fe69")]
 
    public class OurCustomTimerJobFeatureEventReceiver : SPFeatureReceiver
 
    {
 
        const string JobName = "Site Creation Timer";
 
        // Uncomment the method below to handle the event raised after a feature has been activated.
 
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
 
        {
 
            try
 
            {
 
                SPSecurity.RunWithElevatedPrivileges(delegate ()
 
                {
 
                    SPWebApplication parentWebApp = (SPWebApplication)properties.Feature.Parent;
 
                    SPSite site = properties.Feature.Parent as SPSite;
 
                    DeleteExistingJob(JobName, parentWebApp);
 
                    CreateJob(parentWebApp);
 
                });
 
            }
 
            catch (Exception ex)
 
            {
 
                throw ex;
 
            }
 
        }
 
        private bool CreateJob(SPWebApplication site)
 
        {
 
            bool jobCreated = false;
 
            try
 
            {
 
                OurCustomTimerJob job = new OurCustomTimerJob(JobName, site);
 
                SPMinuteSchedule schedule = new SPMinuteSchedule();
 
                schedule.BeginSecond = 0;
 
                schedule.EndSecond = 59;
 
                schedule.Interval = 5;
 
                job.Schedule = schedule;
 
                job.Update();
 
            }
 
            catch (Exception)
 
            {
 
                return jobCreated;
 
            }
 
            return jobCreated;
 
        }
 
        public bool DeleteExistingJob(string jobName, SPWebApplication site)
 
        {
 
            bool jobDeleted = false;
 
            try
 
            {
 
                foreach (SPJobDefinition job in site.JobDefinitions)
 
                {
 
                    if (job.Name == jobName)
 
                    {
 
                        job.Delete();
 
                        jobDeleted = true;
 
                    }
 
                }
 
            }
 
            catch (Exception)
 
            {
 
                return jobDeleted;
 
            }
 
            return jobDeleted;
 
        }
 
        // Uncomment the method below to handle the event raised before a feature is deactivated.
 
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
 
        {
 
            lock (this)
 
            {
 
                try
 
                {
 
                    SPSecurity.RunWithElevatedPrivileges(delegate ()
 
                    {
 
                        SPWebApplication parentWebApp = (SPWebApplication)properties.Feature.Parent;
 
                        DeleteExistingJob(JobName, parentWebApp);
 
                    });
 
                }
 
                catch (Exception ex)
 
                {
 
                    throw ex;
 
                }
 
            }
 
        }
 
        // Uncomment the method below to handle the event raised after a feature has been installed.
 
        //public override void FeatureInstalled(SPFeatureReceiverProperties properties)
 
        //{
 
        //}
 
        // Uncomment the method below to handle the event raised before a feature is uninstalled.
 
        //public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
 
        //{
 
        //}
 
        // Uncomment the method below to handle the event raised when a feature is upgrading.
 
        //public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, System.Collections.Generic.IDictionary<string, string> parameters)
 
        //{
 
        //}
 
    }
 
}
Debugging Timer Job: Debugging a timer job is different than debugging a solution. If you want to debug a timer job you can follow below article.

https://www.enjoysharepoint.com/how-to-debug-timer-job-in-sharepoint-2016-using-visual-studio-2015/

Deploy Timer Job: To deploy a timer job, right click on the timer job and then click on Deploy. Once it is deployed successfully.

Open SharePoint 2016 central administration, then click on Manage web application which is under Application Management.  Then select the particular web application and then  click on Manage Features and you should be able to see our feature which has been published like below:

Now to see the timer job, open SharePoint 2016 central administration, then click on Monitoring and then click on "Review job definitions" which is under Timer Jobs. You can see your custom timer job like below:

Click on the timer job to see details about it:


Once the timer job runs successfully, you can see it will create subsites like below:

Hope this article will be helpful and any suggestions and comments are welcome.