Problem Activating Sharepoint Timer Job - sharepoint

I have created a very simple sharepoint timer job. All i want it to do is iterate through a list and update each list item so that it triggers an existing workflow that works fine. In other words all i am trying to do is work around the limitation that workflows cannot be triggered on a scheduled basis. I have written a class that inherits from SPJobDefinition that does the work and i have a class that inherits from SPFeatureReceiver to install and activate it. I have created the feature using SPVisualdev that my coleagues have used in the past for other SP development.
My Job class is below:
public class DriverSafetyCheckTrigger : SPJobDefinition
{
private string pi_SiteUrl;
public DriverSafetyCheckTrigger(string SiteURL, SPWebApplication WebApp):base("DriverSafetyCheckTrigger",WebApp,null, SPJobLockType.Job)
{
this.Title = "DriverSafetyCheckTrigger";
pi_SiteUrl = SiteURL;
}
public override void Execute(Guid targetInstanceId)
{
using (SPSite siteCollection = new SPSite(pi_SiteUrl))
{
using (SPWeb site = siteCollection.RootWeb)
{
SPList taskList = site.Lists["Driver Safety Check"];
foreach(SPListItem item in taskList.Items)
{
item.Update();
}
}
}
}
}
And the only thing in the feature reciever class is that i have overridden the FeatureActivated method below:
public override void FeatureActivated(SPFeatureReceiverProperties Properties)
{
SPSite site = Properties.Feature.Parent as SPSite;
// Make sure the job isn't already registered.
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
{
if (job.Name == "DriverSafetyCheckTrigger")
job.Delete();
}
// Install the job.
DriverSafetyCheckTrigger oDriverSafetyCheckTrigger = new DriverSafetyCheckTrigger(site.Url, site.WebApplication);
SPDailySchedule oSchedule = new SPDailySchedule();
oSchedule.BeginHour = 1;
oDriverSafetyCheckTrigger.Schedule = oSchedule;
oDriverSafetyCheckTrigger.Update();
}
The problem i have is that when i try to activate the feature it throws a NullReferenceException on the line oDriverSafetyCheckTrigger.Update(). I am not sure what is null in this case, the example i have followed for this is this tutorial. I am not sure what I am doing wrong.

I ran your code in a console application and got the following exception when calling .Update()
"DriverSafetyCheckTrigger cannot be deserialized because it does not have a public default constructor"
Maybe that will help?

I have similar code that is working in one of my Feature Receivers:
string jobName = "MyJobDefinition";
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
{
if (job.Name == jobName)
{
job.Delete();
}
}
SPDailySchedule schedule = new SPDailySchedule();
schedule.EndHour = 2;
schedule.EndMinute = 59;
schedule.EndSecond = 59;
SPJobDefinition jobDefinition = new MyJobDefinition(jobName, site.WebApplication);
jobDefinition.Schedule = schedule;
jobDefinition.Update();
I wonder if your problem is with the schedule. Try setting BeginMinute and maybe also BeginSecond. You could also try setting the End values in conjunction with or instead of the Begin values.

Related

SP2013 TimerJob constructor SPServer parameter

I have a timer job and I am trying to run this timer job on specific server, below is the code I am trying to use to compare server name and creating instance of timer job on FeatureActivated event. I have no idea how to do this. Please help me and correct me if I am doing it totally wrong.
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
// Get an instance of the SharePoint farm.
//SPFarm farm = SPFarm.Local;
SPWebApplication webApp = properties.Feature.Parent as SPWebApplication;
// Remove job if it exists.
DeleteJobAndSettings(webApp);
var serverName = SPServer.Local.DisplayName;
if (string.Equals("sp2013", serverName, StringComparison.OrdinalIgnoreCase))
{
// Create the job.
MyReportNew job = new MyReportNew(webApp, SPServer.Local);
//Other code
}
}
Firstly, you need to use SPServerJobDefinition class.
Secondly, retrieve SPServer object from SPFarm.Local.Servers collection.
For example:
public class CustomJob : SPServerJobDefinition
{
public CustomJob()
: base()
{
}
public CustomJob(string jobName, SPServer server)
: base(jobName, server)
{
this.Title = jobName;
}
public override void Execute(SPJobState state)
{
// do stuff
}
}
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
// Get an instance of the SharePoint farm.
SPFarm farm = SPFarm.Local;
// Remove job if it exists.
DeleteJobAndSettings(webApp);
// Create the job.
MyReportNew job = new MyReportNew("MyJobName", farm.Servers["sp2013"]);
//Other code
}

Sharepoint 2010 event receiver and workflow not starting

I created an event receiver that should trigger a SharePoint designer workflow, however this never happens. This part of the code never evaluates to true for the guid and workflow association: if (association.BaseId == workflowGuid). I am a bit stumped and not quite sure why it's not working. Any insight would be appreciated...and my code is below. Basically when an item is added to my list, it should trigger my Sharepoint designer workflow but this never happens and I can manually start the workflow fine.
public override void ItemAdded(SPItemEventProperties properties)
{
base.ItemAdded(properties);
startWF(properties);
}
private void startWF(SPItemEventProperties properties)
{
try
{
//bool startWF = false;
string strWorkflowID = "{c66ff94f-ba7c-4495-82fe-e73cdd18bad9}";
//string statusVal = properties.AfterProperties["eRequest Division"].ToString();
SPListItem li = properties.ListItem;
//using (SPWeb web = li.Web)
using (SPWeb web = properties.OpenWeb())
{
using (SPSite site = web.Site)
{
SPWorkflowManager mgr = site.WorkflowManager;
SPList parentList = li.ParentList;
//SPList list = web.Lists["Charitable and Political"];
SPWorkflowAssociationCollection associationCollection = parentList.WorkflowAssociations;
LookUpAndStart(li, mgr, associationCollection, "{c66ff94f-ba7c-4495-82fe-e73cdd18bad9}");
}
}
}
catch (Exception ex)
{
}
}
private static void LookUpAndStart(SPListItem listItem, SPWorkflowManager mgr, SPWorkflowAssociationCollection associationCollection, string workflowID)
{
foreach (SPWorkflowAssociation association in associationCollection)
{
Guid workflowGuid = new Guid(workflowID);
//if (association.Name.ToLower().Equals(workflowGuid))
//if (association.BaseId == workflowGuid)
//if (association.BaseId.ToString("B").Equals(workflowGuid))
if (association.BaseId == workflowGuid)
{
string data = association.AssociationData;
SPWorkflow wf = mgr.StartWorkflow(listItem, association, data, true);
break;
}
}
}
}
}
In my case the following was the problem.
Declarative workflows will not start automatically if the following conditions are true:
The Windows SharePoint Services Web application runs under a user's domain account.
The user logs in by using this domain account.
The site displays the user name as System Account.

EventReceiver to Create folder sharepoint 2010

I have this event receiver c# class that I am trying to implement on a Sharepoint site. It did not work. I have deployed it from visual studio 2010 after it was build ok. Does anyone see what is the problem? Is the code ok? or is the problem on the SP? Thank you.
- here is the new code
using System;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.Workflow;
namespace EventReceiverCFolder.EventReceiver1
{
/// <summary>
/// List Item Events
/// </summary>
public class EventReceiver1 : SPItemEventReceiver
{
/// <summary>
/// An item is being added.
/// </summary>
public override void ItemAdded(SPItemEventProperties properties)
{
try
{
if (properties.ListTitle == "CF") // list where the item was added
{ // if item was added to this list then create a folder on - Dlib - list
UpdateFolder(properties);
}
}
catch (Exception ex)
{
properties.Status = SPEventReceiverStatus.CancelWithError;
properties.ErrorMessage = ex.Message;
properties.Cancel = true;
}
}
private void UpdateFolder(SPItemEventProperties properties)
{
string foldername = properties.ListItem["Title"].ToString();
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
//inside RunWithElevatedPriviliges I need to open a new site (an elevated site)
using (SPSite site = new SPSite(properties.Web.Site.ID))
{
using (SPWeb web = site.OpenWeb())
{
web.AllowUnsafeUpdates = true;
SPList list = web.Lists.TryGetList("DLib"); // this is doc Library where the new folder will be created
//note that we are creating a list item, not a folder - a folder IS a list item
SPListItem createdFolder = list.Items.Add(list.RootFolder.ServerRelativeUrl, SPFileSystemObjectType.Folder, null);
if (createdFolder != null)
{
createdFolder["Name"] = foldername;
createdFolder.Update();
}
list.Update();
}
}
});
}
finally { }
}
}
}
Don't do this: SPUser privilegedAccount = properties.Web.AllUsers[#"SHAREPOINT\SYSTEM"];
Read up on using SPSecurity.RunWithElevatedPrivileges. See the MSDN documentation here.
Also don't do a using (SPSite... and inside the using block you try to get the web via SPContext.Current - that web won't be elevated anymore.
The correct way is something along these lines (I didn't try this, so it' just to give you an idea where you are headed):
private void UpdateFolder(SPItemEventProperties properties)
{
string foldername = properties.ListItem["Title"].ToString();
SPSecurity.RunWithElevatedPrivileges(delegate()
{
//inside RunWithElevatedPriviliges I need to open a new site (an elevated site)
using (SPSite site = new SPSite(properties.Web.Site.ID))
{
using (SPWeb web = site.OpenWeb())
{
web.AllowUnsafeUpdates = true;
SPList list = web.Lists.TryGetList("ListTitle"); //is that really the list title?
//note that we are creating a list item, not a folder - a folder IS a list item
SSPListItem createdFolder = list.Items.Add(list.RootFolder.ServerRelativeUrl, SPFileSystemObjectType.Folder, null);
if (newFolder != null)
{
createdFolder["Name"] = foldername;
createdFolder.Update();
}
list.Update();
}
}
});
}
Also try to debug your code, set breakpoints etc.
I had to get folder name like this:
string foldername = Convert.ToString(properties.AfterProperties["Title"]);
Did you try to debug it? try to debug and tell us what error you are getting.
BUT before you debug first use sharepoint manager to see if your event receiver is attached properly.
If you dont know how to debug sharepoint event receiver then please see this

How to access a list in a website from a timer job

I'm creating a timer Job. And i have to access some lists which are in my solution stored in the site : "http://server:9090/sites/thesite"
For the moment, in my Timer Job i use this :
SPWebApplication webApplication = this.Parent as SPWebApplication;
SPContentDatabase contentDb = webApplication.ContentDatabases[contentDbId];
SPList ParametresTech = contentDb.Sites["sites/thesite"].RootWeb.Lists[Constantes.Listes.PARAMETRES_TECHNIQUES.Name];
The problem i'm facing here is that i'm in my development environnement, and i don't know what will be the url of the site they will use to deploy the solution in production.
So is there a way to get to the list without knowing the name of the site ?
Thanks
EDIT :
That's how the timer job is activated :
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
string ListJobName = "SAPToSQL";
SPSite site = properties.Feature.Parent as SPSite;
// make sure the job isn't already registered
foreach (SPJobDefinition job in site.WebApplication.JobDefinitions)
{
if (job.Name == ListJobName)
job.Delete();
}
// install the job
TimerJobSAPToSP listLoggerJob = new TimerJobSAPToSP(ListJobName, site.WebApplication);
SPHourlySchedule schedule = new SPHourlySchedule();
schedule.BeginMinute = 0;
schedule.EndMinute = 59;
listLoggerJob.Schedule = schedule;
listLoggerJob.Update();
}
I would definitely identify the site collection using the feature ID that creates the timer job rather than by URL. Not only does this give you flexibility in naming sites, it also allows you to process multiple site collections that have each subscribed to the job.
I wrote the following utility method to collect the site collections for a timer job:
public static List<Guid> GetSiteIDs(SPWebApplication webApplication, Guid featureId)
{
List<Guid> ids = new List<Guid>();
foreach (SPSite site in webApplication.Sites)
{
try
{
if (SPSite.Exists(new Uri(site.Url))
&& null != site.Features[featureId])
{
try
{
ids.Add(site.ID);
}
catch (Exception ex)
{
// Handle Exception
}
}
}
finally
{
site.Dispose();
}
}
return ids;
}
In the featureId parameter, I pass a constant that I declare in my job definition class.
For more information see: Scope of a feature activated Custom Sharepoint-Timer Job

SPListItem.Update Fails

Using the following block of code, the listItem.Update fails with a NullReferenceException:
SPWeb web = null;
SPList list = null;
SPListItem listItem = null;
try
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite site = new SPSite(this.SiteUrl))
{
web = site.OpenWeb();
list = web.Lists[this.ListName];
listItem = list.Items.Add();
listItem["Background"] = "foo";
}
}
);
listItem.Update();
}
catch
{
}
finally
{
web.Dispose();
}
If I move the listItem.Update() method inside of the anonymous delegate, I get "Operation is not valid due to the current state of the object."
Yes, I've combed through SO and have tried many permutations without success.
Any ideas?
Update:
After the first comment, I tried to remove the anonymous delegate from the code to see if it fared any better:
// store the selected item to pass between methods
public T SelectedItem { get; set; }
// set the selected item and call the delegate method
public virtual void Save(T item)
{
SelectedItem = item;
try
{
SPSecurity.RunWithElevatedPrivileges(SaveSelectedItem);
}
catch
{
}
}
public virtual void SaveSelectedItem()
{
if (SelectedItem != null)
{
using (SPSite site = new SPSite(this.SiteUrl))
{
using(SPWeb web = site.OpenWeb())
{
SPList list = web.Lists[this.ListName];
SPListItem listItem = list.Items.Add();
//UpdateListItem(listItem, SelectedItem);
listItem["Background"] = "foo";
listItem.Update();
}
}
}
}
And this still fails "Operation is not valid due to the current state of the object." In both code samples, it looks like site.Impersonating is false. I am using Windows Auth, and Impersonation in the web.config. This is running from the ASP.Net Development server.
I found an example from this site (blackninjasoftware). I create a reference to the site, grab its SystemAccount token and then create another reference to the site, using the admin token. It seemed a little hackish to me at first - but hey - I have a deadline.
Final working method body now looks like:
SPListItem new_item = null;
SPSite initialSite = new SPSite(this.SiteUrl);
using (var site = new SPSite(this.SiteUrl, initialSite.SystemAccount.UserToken))
{
// This code runs under the security context of the SHAREPOINT\system
// for all objects accessed through the "site" reference. Note that it's a
// different reference than SPContext.Current.Site.
using (var elevatedWeb = site.OpenWeb())
{
elevatedWeb.AllowUnsafeUpdates = true;
SPList list = elevatedWeb.Lists[this.ListName];
new_item = list.Items.Add();
UpdateListItem(new_item, item);
if (new_item != null)
{
new_item.Update();
}
}
}
initialSite.Dispose();

Resources