Tuesday, September 29, 2009

The platform does not know how to deserialize an object of type Microsoft.SharePoint.SPWeb

Hi all,

I have very weird error when I was developing a timer job.
Let me show how I had successfully provision a timer job in WebApplication socpe and changed the different scope (Web) of timer job which totally screwed my SharePoint instance.

This is working code : (WebApplication Scope)
class AlertNotificationInstaller : SPFeatureReceiver
{

private const string JobNameDaily =
ChecklistsConstants.JobNameDaily;
private const string JobNameWeekly =
ChecklistsConstants.JobNameWeekly;


public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWebApplication webApp =
properties.Feature.Parent as SPWebApplication;
if (webApp == null)
throw new SPException("Error obtaining refrence to the Web application.");

#region Delete other Job Definition for System Checklists
foreach (var job in webApp.JobDefinitions)
{
if (job.Name == JobNameDaily)
{
job.Delete();
}

if (job.Name == JobNameWeekly)
{
job.Delete();
}

}
#endregion



#region Create instances for timer jobs
AlertNotificationJob dailyJob = new AlertNotificationJob(JobNameDaily, webApp);
dailyJob.Properties.Add("Daily Job", "Tools");

AlertNotificationJob weeklyJob = new AlertNotificationJob(JobNameWeekly, webApp);
weeklyJob.Properties.Add("Weekly", "Tools");

#endregion

#region Daily


SPDailySchedule daily = new SPDailySchedule();
daily.BeginHour = 10;
daily.BeginMinute = 28;
daily.BeginSecond = 00;

daily.EndHour = 10;
daily.EndMinute = 30;
daily.EndSecond = 00;
dailyJob.Schedule = daily;
dailyJob.Update();



#endregion

#region Weekly

//SAME CODE FOR weekly schedule SPWeeklySchedule


#endregion


}


}

But need to change the time job to the web scope and changed the code as follows:
class AlertNotificationInstaller : SPFeatureReceiver
{

private const string JobNameDaily = ChecklistsConstants.JobNameDaily;
private const string JobNameWeekly = ChecklistsConstants.JobNameWeekly;

private SPWeb web = null;
private SPSite site = null;
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{

web = properties.Feature.Parent as SPWeb;
site = web.Site;
if (web == null)
throw new SPException("Error obtaining refrence to the Web .");

#region Delete other Job Definition for System Checklists
foreach (var job in site.WebApplication.JobDefinitions)
{
if (job.Name == JobNameDaily)
{
job.Delete();
}

if (job.Name == JobNameWeekly)
{
job.Delete();
}

}
#endregion

#region Create instances for timer jobs
AlertNotificationJob dailyJob = new AlertNotificationJob(JobNameDaily,site.WebApplication);
dailyJob.Properties.Add("Daily Job", "Tools");

AlertNotificationJob weeklyJob = new AlertNotificationJob(JobNameWeekly, site.WebApplication);
weeklyJob.Properties.Add("Weekly", "Tools");

#endregion

#region Daily

SPDailySchedule daily = new SPDailySchedule();
daily.BeginHour = 10;
daily.BeginMinute = 28;
daily.BeginSecond = 00;

daily.EndHour = 10;
daily.EndMinute = 30;
daily.EndSecond = 00;
dailyJob.Schedule = daily;
dailyJob.Update();

#endregion

#region Weekly

//Weekly Schedule 

#endregion

}
}
And I did some code chnages as follows:

>> In the feature.xml, I changed the scope from "WebApplication" to "Web".

Scope="WebApplication" to Scope="Web"

>> In Execute method, I changed the scope of "WebApplication" to just "Web"

// get a reference to the current web application
SPWebApplication webApplication = this.Parent as SPWebApplication; TO

// get a reference to the current web application
SPWeb web= this.Parent as SPWeb;


I just upgraded the solution and timer job was provisioned in the web scope.
But when I deactivate this feature I got this error:

The platform does not know how to deserialize an object of type Microsoft.SharePoint.SPWeb. The platform can deserialize primitive types such as strings, integers, and GUIDs; other SPPersistedObjects or SPAutoserializingObjects; or collections of any of the above. Consider redesigning your objects to store values in one of these supported formats, or contact your software vendor for support.


There few other observations:

1) I try the my deactivate the feature on the web scope (through UI) , it threw the same error.
2) Tried uninstall the solution through WSPBuider, it threw this error:

i) Error while killing the timer jobs
ii) Same error mentioned above.

3) Tried delete the solution from Central Admin . Same Error.
4) When I went to Central Admin >> Operations >> Timer Job Definitions and clicked on it, threw this error:


ERROR:

The platform does not know how to deserialize an object of type Microsoft.SharePoint.SPWeb. The platform can deserialize primitive types such as strings, integers, and GUIDs; other SPPersistedObjects or SPAutoserializingObjects; or collections of any of the above. Consider redesigning your objects to store values in one of these supported formats, or contact your software vendor for support.

Now, I have noticed that all the Times Job definitions are screwed up.

I also researched and found that one person had same issue and workaround:
Issue and workaround .


I referred to one the blog which discussed this issue.
http://jritmeijer.spaces.live.com/blog/cns!8A48A27460FB898A!1176.entry

Apparently, SharePoint UI cant help . I have to modify the Database (SharePoint Configuration database ).
After studying the schema of the Config Database, SharePoint uses Hierarchical Object Store (HOS).

There are 2 important tables
1) Classes
2) Objects


What I queried the tables (Classes and Objects) and identified the "corrupt" object. (its the name of the solution , in my case)

>> Deleted the corrupt object . (Objects table)
>> Delete the corresponding class. (Classes table)

Went to Central Admin >> Operations >> Timer Job Definitions .

VOILA!!!

It worked....

I got my lesson and bottom line is that:

>> Whenever you are changing the scope, you MUST DEACTIVATE the feature first and then upgrade the solution. Otherwise, it will screw the SharePoint instance.

Cheers
--Aroh

5 comments:

Hemendra said...

Hi,
This is first time when i am creating a timer job. I referring your logic but unable to estimate of "ChecklistsConstants". Can you please help me..

1. What is ChecklistsConstants
2. How can implement it

Aroh Shukla said...

Hi ,,,

ChecklistConstants is a for keeping all the constants such as defining the timer job definitions, some other constants for my hard coded values, defining the timeperiod etc. In fact, in this file I have not implemented timer job logic. Its just for ease and if later on if we want to change the constants, we can do it on this file. Other files will not be affected.

A basic ChecklistConstants.cs file will look like this:

--------------------------------
File: ChecklistsConstants.cs

namespace AlertNotificationJob
{
public class ChecklistsConstants
{
#region Timer Job Definitions

public const string JobNameDaily = "[IT] System Checklists Daily";
public const string JobNameWeekly = "[IT] System Checklists Weekly";

#endregion

#region Define time period

public const string Daily = "Daily";
public const string Weekly = "Weekly";
public const string Monthly = "Monthly";
public const string Quarterly = "Quarterly";
public const string Yearly = "Yearly";

#endregion

}
}

---------------------------------

For implementing timer jobs, we need to two files:

1) Creating Job Definitions and instances.

Its standard file where we define the job definitions and create instances timer job. In this blog I have already defined it. You can refer the code AlertNotificationInstaller.cs.

2) Define the business logic.
In this part, you have to define your business logic.

Functional part:
In my case, I have a list called as "Checklists alerts" where administrator will users and corresponding responsible for different IT sections

For e.g. Checklist Alerts will look this

USER TEAM PERIOD
Aroh Shukla Backup Daily
Aroh Shukla Backup Weekly
Alex Steve Servers Daily
Alex steve Storage Monthly


In this part, you have to define your business logic.

Aroh Shukla said...

Technical part:

We have to use a method called as "Execute" (Part of SharePoint SDK)
This is my code with the business logic with the timer jobs

-----
class AlertNotificationJob : SPJobDefinition
{
#region Variables

public string siteName = string.Empty;
public string webName = string.Empty;

private string timePeriod = string.Empty;
private string template = string.Empty;

#endregion

#region Constructors

public AlertNotificationJob()
: base()
{
}

public AlertNotificationJob(string jobName, SPService service, SPServer server, SPJobLockType targetType)
: base(jobName, service, server, targetType)
{
}

public AlertNotificationJob(string jobName, SPWebApplication webApplication)
: base(jobName, webApplication, null, SPJobLockType.ContentDatabase)
{
this.Title = jobName;
}

#endregion

public override void Execute(Guid contentDbId)
{

siteName = Properties["ApacSite"].ToString();
webName = Properties["ToolsSite"].ToString();


// get a reference to the current web application
SPWebApplication webApplication = this.Parent as SPWebApplication;

// get a reference to the current site collection's content database
SPContentDatabase contentDb = webApplication.ContentDatabases[contentDbId];

foreach (SPSite site in contentDb.Sites)
{
Console.WriteLine(site.ServerRelativeUrl);
Console.WriteLine(site.RootWeb.ToString());

Console.WriteLine(Properties["ApacSite"].ToString());
Console.WriteLine(Properties["ToolsSite"].ToString());

if (site.RootWeb.ToString().Equals(Properties["ApacSite"].ToString()))
{
siteName = site.ServerRelativeUrl;
break;
}

}

Aroh Shukla said...

// get a reference to the Site Collection in the content database
using (SPSite spSite = contentDb.Sites[siteName])
{
// get a reference to the Web
using (SPWeb spWebRoot = spSite.OpenWeb(this.Properties["ToolsSite"].ToString()))
{

// get a reference to the "Alerts" list (LIST 1)
SPList alerts = spWebRoot.Lists["Checklists Alerts"];

// get a reference to the "System Checklists" list (LIST 2)
SPList checkList = spWebRoot.Lists["System Checklists"];

// get the query object for user alert
SPQuery queryUserAlert = new SPQuery();

Aroh Shukla said...

#region Daily
// Define the DAILY which is defined in ChecklistsConstants.cs

timePeriod = ChecklistsConstants.Daily;
// Query the "Checklists Alert" list

queryUserAlert.Query = "<Where>" +

"<Eq>" +

"<FieldRef Name='Period' />" +

"<Value Type='Choice'>" + timePeriod + " </Value>" +

"</Eq>" +

"</Where>";

// Count number of users

SPListItemCollection noOfUsers = alerts.GetItems(queryUserAlert);
// Get all the users for DAILY

foreach (SPListItem item in noOfUsers)

{

// Use "GetUser" function for List "item" into a SPUser ojbect

SPUser user = GetUser(item, item.Fields["User"]);

string userName = user.Name;
//Get the Template

template = item["Template"].ToString();



// Create a SPQuery instance for "System Checklists"

SPQuery queryChecklists = new SPQuery();
// Query the "System Checklists"

queryChecklists.Query = "<Where>" +

"<And>" +

"<Eq>" +

"<FieldRef Name='Template' />" +

"<Value Type='Text'>" + template + "</Value>" +

"</Eq>" +

"<And>" +

"<Eq>" +

"<FieldRef Name='Time_x0020_Period' />" +

"<Value Type='Text'>" + timePeriod + "</Value>" +

"</Eq>" +

"<And>" +

"<Eq>" +

"<FieldRef Name='Editor' />" +

"<Value Type='User'>" + userName + "</Value>" +

"</Eq>" +

"<Eq>" +

"<FieldRef Name='Modified' />" +

"<Value IncludeTimeValue='FALSE' Type='DateTime'>" + SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Today) + "</Value>" +

"</Eq>" +

"</And>" +

"</And>" +

"</And>" +

"</Where>";


//Get the no of items from the Query

SPListItemCollection noOfItems = checkList.GetItems(queryChecklists);
//if user does not update his/her checklist then send a email reminder

if (noOfItems.Count == 0)

{

string daily = "Daily Checklists: " + template ;

SendEmailReminder(spWebRoot, user.Email, user.Name, daily);

}
}


#endregion


}
}
}
}
}


Let me know if you still have issue in implementing timer jobs.

Cheers
--aaroh

Low Code Reimagined with AI + Copilot Pitch Deck - Copy Copilot day (Virtual) - 2023

 Hi All,  I presneded a session at Pune UG on Low Code Reimagined with AI + Copilot Pitch Deck.  Video is at this address  https://www.youtu...