SPListItem.Tasks always empty - c#

I have a custom sharepoint (2007) list (named testlist) on which I attached a test workflow (built with sharepoint designer 2007 and named testwf), which only task defined in the 'Actions' section at 'Step 1' is to wait until april 2014.
When I add a new item to the testlist the testwf will start and, when I switch to the grid view, the item has the field "testwf" as running.
Now I need to access the workflow associated with the item and then "complete" this task via code by changing its status but, using the following code, I always get the item.Tasks list empty (but I can see that the internal variable m_allTaskListTasks has 1 element).
using (SPSite site = new SPSite("http://mysp"))
{
site.AllowUnsafeUpdates = true;
SPWeb web = site.OpenWeb();
web.AllowUnsafeUpdates = true;
foreach (SPList list in web.Lists)
{
if (list.Title != "testlist") continue;
foreach (SPListItem item in list.Items)
{
item.Web.AllowUnsafeUpdates = true;
if(item.Tasks.Count > 0)
//do work
}
}
}
Maybe I'm missing something...

I use this code to access my workflowtasks:
Guid taskWorkflowInstanceID = new Guid(item["WorkflowInstanceID"].ToString());
SPWorkflow workflow = item.Workflows[taskWorkflowInstanceID];
// now you can access the workflows tasks
SPTask task = workflow.Tasks[item.UniqueId];

Cross-posted question.
#petauro, have you made any headway on this? I can corroborate #moontear's answer based on the following code that I have used with success in the past:
...
// get workflow tasks for SPListItem object item
if (item != null && item.Workflows != null && item.Workflows.Count > 0)
{
try
{
var workflows = site.WorkflowManager.GetItemActiveWorkflows(item);
foreach (SPWorkflow workflow in workflows)
{
// match on some indentifiable attribute of your custom workflow
// the history list title is used below as an example
if (workflow.ParentAssociation.HistoryListTitle.Equals(Constants.WORKFLOW_HISTORY_LIST_TITLE))
{
var workflowTasks = workflow.Tasks;
if (workflowTasks != null && workflowTasks.Count > 0)
{
// do work on the tasks
}
}
}
}
catch
{
// handle error
}
}
...
While only slightly different from the code you posted in your latest comment, see if it helps.
Another minor point: are there multiple instances of lists titled "testlist" within your SPWeb? If not, why iterate over web.Lists? Just get the one list directly and avoid some superfluous CPU cycles: SPWeb.GetList()

You have to go differently about this. You need to get the workflow task list and retrieve your task from there and finish it.
First you would need to check whether a workflow is running on your item: if (item.Workflows > 0) from there you could iterate through all the workflow instances on the list item, get the SPWorkflowAssociation and the associated task and history list. From there you would only need to find the task you are looking for in the associated task list.

Related

How to load messages faster? EWS C#

I have a question about loading methods for EWS. I have a lot of messages in my outlook and I'm also using EWS and WinForms.
Right know my code is this:
while (more)
{
FindItemsResults<Item> findResults1 = allItemsFolder.FindItems("System.Message.DateReceived:01/01/2011..12/31/2022", iv);
foreach (var item in findResults1)
{
if (item.GetType() == typeof(Microsoft.Exchange.WebServices.Data.EmailMessage))
{
listik.Add(item);
}
}
more = findResults1.MoreAvailable;
if (more)
{
iv.Offset += 1000;
}
}
It took 12 minutes already for me for running my application and I decided to stop it, because something is wrong. I think I have more than 61k messages.
The idea is to show only those messages, which have my criteria, like email address. So, I should go through all the messages and folders and put suitable in one list to show them. I'm not sure how to do this better.
Anyone can help me with that?
Edit//Added more code
PropertySet psPropSet = new PropertySet(BasePropertySet.FirstClassProperties)
{
RequestedBodyType = BodyType.Text
};
Added more code:
foreach (var item in listik)
{
if (item != null)
{
if (((EmailMessage)(item))?.From?.Address != null)
{
var d = ((EmailMessage)(item)).From.Address;
if (d.Contains(comboBox2.SelectedItem.ToString()))
{
item.Load(psPropSet);
emails.Add((EmailMessage)item);
if (item.Subject != null)
{
listBox1.Items.Add(item.Subject);
}
}
}
}
}
I found a link from Microsoft:
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/email-properties-and-elements-in-ews-in-exchange
It's about first-class properties and I can see here EWS element - FROM, but I can't figure out how to set it in iv.PropertySet.
I would try to remove the search and try again its pretty pointless anyway with such a broad date range, the all items folder is a search folder so your applying one search on top of the other which generally doesn't work well. Also use a property set to limit the result that is returned to just the properties you want which will also make the query run faster eg
iv.PropertySet =
new PropertySet(
BasePropertySet.IdOnly,
ItemSchema.Subject);
No email app, including Outlook, ever loads all messages in a folder. Use a virtual listview/grid and retrieve the messages (using the appropriate offset) only when displaying them to an end user.
I found the way how it's working. Thanks, everyone for your help.
iv.PropertySet = new PropertySet(EmailMessageSchema.From, ItemSchema.Subject);
while (more)
{
FindItemsResults<Item> findResults1 = allItemsFolder.FindItems(iv);
foreach (var item in findResults1)
{
if (item.GetType() == typeof(Microsoft.Exchange.WebServices.Data.EmailMessage))
{
listik.Add(item);
}
}
more = findResults1.MoreAvailable;
if (more)
{
iv.Offset += 1000;
}
}

Process multiple Tasks return values in Task Parallel Library

I am having a problem in processing multiple tasks return values written in Task Parallel Library.
My code does as below:
. Creates set of Keys and objects with data coming from third party
. It starts task for each key in that dictionary
. In each of the task, again set of tasks will be started with user objects associated with that key (from step 2 above)
. Once all the inner tasks are executed, I want to update the user object based on the return value.
So far I have come up with the below code:
void ProcessKeys(ExchangeKey eKey)
{
// Create List of Tasks
var retValues = new List<Task<ReturnStatus>>();
// The ContainsKey here is commented out but I have a comparer method which can compare the compound keys
if (TaskProcessor.lstInputsFromUser.Count > 0 )// && TaskProcessor.lstInputsFromUser.ContainsKey(eKey))
{
// Everytime dData will be filled with data from outside world
OrderBook myOrder = new OrderBook();
// Remove the key as the next object will be inserted somewhere else when new data arrives
dData.TryGetValue(eKey, out myOrder);
if (myOrder != null)
{
foreach (var indiElement in TaskProcessor.lstInputsFromUser[eKey])
{
// If the data is already run for the expected number of cycles to total cyles, don't run again
if (indiElement.NoOfCycles != indiElement.TotalNoOfCycles)
{
var singleRetVal = new Task<ReturnStatus>( () => ProcessData(myOrder, indiElement));
singleRetVal.Start();
retValues.Add(singleRetVal);
}
}
}
// Wait till all tasks are completed
// Here also I am facing problem of how to wait on the 1-exchangekey and 5-tasks policy
Task.WaitAll(retValues.ToArray());
var results = retValues.Select(t => t.Result)
.ToList();
if (results != null)
{
Console.WriteLine("Shall print two times only");
foreach (var retResult in results)
{
if (TaskProcessor.ReturnStatusAfterCalc.Keys.Contains(retResult.ID))
{
TaskProcessor.ReturnStatusAfterCalc[retResult.ID] = retResult;
var myObj = TaskProcessor.lstInputsFromUser[eKey].FirstOrDefault(indi => indi.ID == retResult.ID);
if (myObj != null)
{
// Here I am unable to update my original object
myObj.NoOfCycles += retResult.CyclesCompleted;
/// arEvent.Set();
// Task.Run( () => Update database with myObj.NoOfCyles value for the listInputsFromUser[eKey]
}
}
else
{
// TaskProcessor.ReturnStatusAfterCalc[retResult.ID] = new ReturnStatus();
TaskProcessor.ReturnStatusAfterCalc.TryAdd(retResult.ID, retResult);
var myObj = TaskProcessor.lstInputsFromUser[eKey].FirstOrDefault(indi => indi.ID == retResult.ID);
if (myObj != null)
{
// Here I am unable to update my original object
myObj.NoOfCycles += retResult.CyclesCompleted;
// Task.Run( () => Update database with myObj.NoOfCyles value for the listInputsFromUser[eKey]
// arEvent.Set();
}
}
}
}
}
}
The main problem is, the code runs multiple times the inner set of tasks than the required ones. Also I am facing problem in updating the actual object with return values as it is executing the inner tasks more than once.
Request anyone to help me on this. While(true) is bad but I will replace this with a set event included with Timer.
After couple of comments, I feel my example and explanation does not actually give problem description. Sorry for the trouble guys.
Problem Description:
say I am having 10 symbols from an exchange. User 1 is subscribed to 3 symbols and User 2 subscribed to 7 symbols. Now exchange will keep on sending data for these 10 symbols, User 1 and User 2 will give me certain price to place orders on these 10-symbols. I will be running 10 tasks to check the price for the incoming 10-symbols. Later User 1 also adds remaining 7 symbol price input. So I have to run 13 different tasks which takes 13 User Objects and 10-symbol data as each user object might contain different price and conditions
EDIT:
With the help from Panagiotis Kanavos, I realised Parallel.ForEach can be used to store the return results and Tasks are not required. The main logic presented here and rest of the unnecessary code is removed as per the comments.

Programmatically Process all Sitecore items with Workflow

I have situation where I have a lot items in the Workbox that I need to clear out.
I need to process these items so that they can be assigned a workflow state (without triggering workflow actions) according to this rule:
If the item has a state of draft or waiting for approval and there is a published version higher than the current version then set the workflow state to be x worflowstate.
I haven't done much experimenting with workflow, so does anybody have any code samples or thoughts of how I could achieve this?
Here is a blog post about Changing workflow state of Sitecore items programmatically .
First you need to find all items in chosen workflow states:
IWorkflow[] workflows = Sitecore.Context.Database.WorkflowProvider.GetWorkflows();
IWorkflow chosenWorkflow = workflows[..]; // choose your worfklow
WorkflowState[] workflowStates = chosenWorkflow.GetStates();
foreach (WorkflowState state in workflowStates)
{
if (!state.FinalState)
{
DataUri[] itemDataUris = chosenWorkflow.GetItems(state.StateID);
foreach (DataUri uri in itemDataUris)
{
Item item = Sitecore.Context.Database.GetItem(uri);
/* check other conditions - newer version exists etc */
ChangeWorkflowState(item, newWorkflowStateId);
}
}
}
The simplest code for changing workflow state of Sitecore item without executing any actions related to the new workflow state is:
public static WorkflowResult ChangeWorkflowState(Item item, ID workflowStateId)
{
using (new EditContext(item))
{
item[FieldIDs.WorkflowState] = workflowStateId.ToString();
}
return new WorkflowResult(true, "OK", workflowStateId);
}
public static WorkflowResult ChangeWorkflowState(Item item, string workflowStateName)
{
IWorkflow workflow = item.Database.WorkflowProvider.GetWorkflow(item);
if (workflow == null)
{
return new WorkflowResult(false, "No workflow assigned to item");
}
WorkflowState newState = workflow.GetStates()
.FirstOrDefault(state => state.DisplayName == workflowStateName);
if (newState == null)
{
return new WorkflowResult(false, "Cannot find workflow state " + workflowStateName);
}
return ChangeWorkflowState(item, ID.Parse(newState.StateID));
}

Any use to grab a SPListItem by multiple means in an event receiver?

I am working on supporting code in a custom event receiver developed about 6 years ago... There was no source code, so we had to reflect the production assembly, and are working on cleaning it up before working.
In the code, I came across this, and I can't tell why it would be done, and I don't know enough about SharePoint to be able to know if there is a reason it has to be done this way, or if the original coder was an idiot (both are possible...)
public override void ItemAdded(SPItemEventProperties properties)
{
Trace.WriteLine("ItemAdded() invoked.");
base.DisableEventFiring();
base.ItemAdded(properties);
try
{
SPContext context = SPContext.GetContext(properties.OpenWeb());
SPUserToken userToken = context.Site.SystemAccount.UserToken;
using (SPSite site = new SPSite(context.Site.ID, userToken))
using (SPWeb web = site.OpenWeb(context.Web.ID))
{
SPListItem listItem = null;
try
{
listItem = web.GetListItem(properties.ListItem.Url);
web.AllowUnsafeUpdates = true;
}
catch
{
Trace.WriteLine("No Url properties, we must be running in CLI mode. Exiting...");
}
if (listItem != null)
{
SPList parentList = listItem.ParentList;
listItem = parentList.GetItemById(properties.ListItemId);
}
...
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.ToString());
}
finally
{
base.EnableEventFiring();
}
}
It just seems like this is getting the SPListItem by multiple ways, when it is passed in in the properties object... Same for the Website.. So, I guess my question is since they end up just getting the List item by the properties anyways through the list, is there really any reason to go through all the other checks/methods to just throw the value away in the end?
The difference is that it's not getting the list item using the current user's credentials; it's grabbing it using the system account's credentials.
The system account may have permissions surrounding the list item that the current user does not.
That said, there are most certainly easier ways of getting the list items, and there's no reason to get it several times like that, even if getting it once may be appropriate.

Stuck on implementation of WebBrowser Control and working with AJAX responses

Here we go...
I am trying to create a bot to walk through various functions of a website I do not have control of. Initially, I thought it would be best to just connect to the database (MySql) that the site is tied to and make my associations there...The database is built so extensively that I can't make out where to point what to where how etc...It's beyond my (DBA)programmer casting... ;)
So, my next idea was, create a bot...simple enough right? First hurdle, not everything on the page carries an ID...bring on the loops...
Got it.
Now I'm stuck with working with my data and page response.
I'm trying to fill out part of a form and perform an AJAX search. The problem is, there is no DocumentCompleted for this. And honestly, that isn't where my problem lies. I've tried using Thread.Sleep, Timers, etc...no avail.
// The app reads categories from a csv file,
// then performs a search for the category
// Search results are only displayed if I break the foreach loop
foreach (var item in bar)
{
var document = wbBrowser.Document;
if (document != null)
{
var name = document.GetElementById("product_filter_name");
if (name != null)
{
name.SetAttribute("value", item.Key.ToString());
var buttons = document.GetElementsByTagName("button");
foreach (HtmlElement button in buttons)
{
var findSearch = button.InnerHtml.IndexOf("Search");
if (findSearch > -1)
{
button.InvokeMember("click");
}
}
}
// This where the problem starts...
// I want the AJAX to run, then perform Step two,
// but the WebBrowser doesn't return the search
// results until the end (break;)
// Step Two
var elems = document.GetElementsByTagName("tr");
foreach (HtmlElement elem in elems)
{
// find particular item in result table
}
break;
// Now the search results display!!!!
// I tried implementing a timer, Thread.Sleep,
// everything I could find via Google before
//starting Step Two, but it hasn't worked!
}
}
The actual browser control has a WebBrowser.OnDocumentCompleted event which you might need to hook into so that you can be alerted when the ajax call has returned back from the server.

Categories