I am having an issue where I am trying to find at least one contact inside any of the outlook folders. I have a recursive function that iterates thru the items inside a folder and if the item is of type contact then we add it to a list. However, this code runs extremely slow when folders have a large number of records say 4000 items.
Is there any way just to get contacts or is there a way to make this code more efficient?
foreach (var item in folderBase.Items)
{
if (returnFirst && result.Count > 0)
break;
if ((item is Outlook.ContactItem))
{
result.Add((Outlook.ContactItem)item);
}
}
Firstly, storing 4000 live Outlook objects in a list is a bad idea: you will run out of RPC channels in case of online Exchange store at item 255. Store the entry ids and use them to call Namespace.GetItemFromID() when you actuqlly need it; then release it as soon as you are done.
Secondly, use MAPIFolder.GetTable - it will let you retrieve values from multiple items without actually opening them; perfect in your case. Try something like the following (VB script):
set Folder = Application.ActiveExplorer.CurrentFolder
set Table = Folder.GetTable("[MessageClass] = 'IPM.Contact' ")
Table.Columns.Add("EntryID")
while not Table.EndOfTable
set Row = Table.GetNextRow()
vEntryId = Row.Item(1)
Debug.Print vEntryId
wend
You need to use the Find/FindNext or Restrict methods of the Items class instead.
Read more about these methods and see the sample code in the following articles:
How To: Use Restrict method in Outlook to get calendar items
How To: Retrieve Outlook calendar items using Find and FindNext methods
You can use the MessageClass property of Outlook items to get the contact items only.
Related
So I have a SP list with about 100k items (voucher codes) in it.
Each of them has columns for State (Active/Used), Value (10,20,30) Group (Normal, Special) and Code (random alphanumeric). Each of the columns are indexed
I can't use CAML to get the next active code for a certain group and value, because each of the criteria would return > 5k items (list view threshold).
So what would be the most efficient way to retrieve the next code?
As the list is continuously growing, loading all items with SPListItemCollectionPosition is not really an option. Isn't there a better way?
It should work for onprem, as well as spOnline
Thank you
If your Code is a progressive number, increasing at any new entered item, you have 2 options:
Create a service List that you can name something like CodeCounter, here you store the last created Code in a column. Create a workflow in main list, starting at item creation. This workflow will read the last created Code from service list, then update it to Code+1 and in parallel manages the update (optional) of main code in main list
use an event handler (farm solution, here the query surely works better)
I am writing a C# application where I want to loop through the 500 most recent emails in a given folder. The reason is because getting all the emails takes a long time using the below lines:
MAPIFolder folder = outlookApp.GetNamespace("MAPI").GetDefaultFolder(OlDefaultFolders.olFolderInbox);
List<MailItem> items = folder.Items.OfType<MailItem>().ToList();
However, I know that what I am searching for each time is always going to be a recent email, so there's no need to get an entire year's worth of emails each time (over 8000, and I get less emails than the average employee at my job).
So, is there a way to only retrieve a certain amount of emails from a folder with Microsoft.Office.Interop.Outlook? Thanks in advance.
Iterating over all items in the folder is not really a good idea:
List<MailItem> items = folder.Items.OfType<MailItem>().ToList();
Instead, you need to use the Find/FindNext or Restrict methods of the Items class. They allow getting only items that correspond to your conditions. Read more about these methods in the following articles:
How To: Use Find and FindNext methods to retrieve Outlook mail items from a folder (C#, VB.NET)
How To: Use Restrict method to retrieve Outlook mail items from a folder
Instead of getting recent 500 emails you may retrieve emails for a day, few days, week and etc. So, you can process items in bunch. For example:
criteria = "[ReceivedTime] > '" _
& Format$("6/12/20 3:30PM","General Date") & "'"
You may find the Filtering Items Using a Date-time Comparison article helpful.
The task is to move multiple items from one folder to another without using any loops (which basically move 1 item at a time and then repeat the process again and again). In fact, I am talking about C# equivalent of choosing multiple items in folder in Outlook and moving them elsewhere. Is there a way of doing this, or am I stuck with loops anyway here? Thanks in advance.
Yes you can move items without using a loop using EWS API.The do while loop used here is to loop through the pages as the ItemView has a pagesize of 100. You can change this logic as you wish. But I haven't looped through the findResults which is a set of emails.
Here I am assuming that the folder 'Test' is under the root folder. If the folder is a subfolder under Inbox change it to WellKnownFolderName.Inbox
List<SearchFilter> searchFilterCollection = new List<SearchFilter>();
searchFilterCollection.Add(new SearchFilter.IsEqualTo(EmailMessageSchema.IsRead, false));
SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(LogicalOperator.And, searchFilterCollection);
ItemView view = new ItemView(100);
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.Subject);
view.Traversal = ItemTraversal.Shallow;
FindItemsResults<Item> findResults;
do
{
findResults = service.FindItems(new FolderId(WellKnownFolderName.Inbox, new Mailbox(user)), searchFilter, view);
var itemIds = from item in findResults
select item.Id;
service.MoveItems(itemIds,
(Folder.Bind(service, WellKnownFolderName.MsgFolderRoot)
.FindFolders(new SearchFilter.IsEqualTo(FolderSchema.DisplayName, "Test"), new FolderView(1))
.FirstOrDefault(x => x.DisplayName == "Test")).Id);
view.Offset = Convert.ToInt32(findResults.NextPageOffset);
} while (findResults.MoreAvailable);
While it would occasionally be nice to be able to move a bunch of messages at once, the simple answer is you can't.
The EWS API does not provide a method for moving a list of messages all at once, only a method for moving an individual message to a different folder. As such you are stuck with looping through your messages and moving them individually.
Which, incidentally, is how Outlook seems to do it... so in that regard you're at least fulfilling that requirement :)
I think you need a loop like foreach, while to iterate through the collection of your selected mail items.
The Outlook object model doesn't provide any method for moving multiple items at once. Use the Move method of the MailItem class for moving items in the loop.
On the Extended MAPI level (C++ or Delphi only), IMAPIFolder::CopyMessages(MESSAGE_MOVE ) takes a list of entry ids and thus allows to move or copy multiple messages in a single call.
Outlook Object Model however only allows to copy or move one message at a time. If using Redemption (I am its author - any language) is an option, it exposes RDOFolder.Items.MoveMultiple / CopyMultiple methods that take a list of ";" or CR/LF separated entry ids or an array of entry id or items.
I'm working on a c# application that extracts metadata from all the documents in a Lotus Notes database (.nsf file, no Domino server) thusly:
NotesDocumentCollection documents = _notesDatabase.AllDocuments;
if (documents.Count>0)
{
NotesDocument document = documents.GetFirstDocument();
while (document!=null)
{
//processing
}
}
This works well, except we also need to record all the views that a document appears in. We iterate over all the views like this:
foreach (var viewName in _notesDatabase.Views)
{
NotesView view = _notesDatabase.GetView(viewName);
if (view != null)
{
if (view.AllEntries.Count > 0)
{
folderCount = view.AllEntries.Count;
NotesDocument document = view.GetFirstDocument();
while (document!=null)
{
//record the document/view cross reference
document = view.GetNextDocument(document);
}
}
Marshal.ReleaseComObject(view);
view = null;
}
}
Here are my problems and questions:
We fairly regularly encounter documents in a view that were not found in NotesDatabase.AllDocuments collection. How is that possible? Is there a better way to get all the documents in a notes database?
Is there a way to find out all the views a document is in without looping through all the views and documents? This part of the process can be very slow, especially on large nsf files (35 GB!). I'd love to find a way to get just a list of view name and Document.UniversalID.
If there is not a more efficient way to find all the document + view information, is it possible to do this in parallel, with a separate thread/worker/whatever processing each view?
Thanks!
Answering questions in the same order:
I'm not sure how this is possible either unless perhaps there's a special type of document that doesn't get returned by that AllDocuments property. Maybe replication conflicts are excluded?
Unfortunately there's no better way. Views are really just a saved query into the database that return a list of matching documents. There's no list of views directly associated with a document.
You may be able to do this in parallel by processing each view on its own thread, but the bottleneck may be the Domino server that needs to refresh the views and thus it might not gain much.
One other note, the "AllEntries" in a view is different than all the documents in the view. Entries can include things like the category row, which is just a grouping and isn't backed by an actual document. In other words, the count of AllEntries might be more than the count of all documents.
Well, first of all, it's possible that documents are being created while your job runs. It takes time to cycle through AllDocuments, and then it takes time to cycle through all the views. Unless you are working on a copy or replica of the database that is is isolated from all other possible users, then you can easily run into a case where a document was created after you loaded AllDocuments but before you accessed one of the views.
Also, is it may be possible that some of the objects returned by the view.getXXXDocument() methods are deleted documents. You should probably be checking document.isValid() to avoid trying to process them.
I'm going to suggest using the NotesNoteCollection as a check on AllDocuments. If AllDocuments were returning the full set of documents, or if NotesNoteCollection does (after selecting documents and building the collection), then there is a way to do this that is going to be faster than iterating each view.
(1) Read all the selection formulas from the views, removing the word 'SELECT' and saving them in a list of pairs of {view name, formula}.
(2) Iterate through the documents (from the NotesNoteCollection or AllDocuments) and for each doc you can use foreach to iterate through the list of view/formula pairs. Use the NotesSession.Evaluate method on each formula, passing the current document in for the context. A return of True from any evaluated formula tells you the document is in the view corresponding to the formula.
It's still brute force, but it's got to be faster than iterating all views.
So I'm creating a SharePoint 2010 project in Visual Studio that contains a Sequential Workflow (Farm Solution only) item that is associated with a certain list so that when an item is added to the list the workflow starts.
My question is, say an item was added in the following format:
Name | Email
Dave | dave#dave.com
Is there a way, programatically, to store this data in variables? As is, without using any hardcoded indexes or anything, just programatically have it so that I can pull this data and store it in two C# variables (int iD, string emailAddress) and the workflow knows which list item kicked it off?
At the moment the way I'm doing it is:
using (SPWeb oWeb = SPContext.Current.Web) {
SPList oList = web["List Name"];
string name = list.Items[(oList.ItemCount - 1)]["Name"].ToString();
}
But I'd rather not use indexers as there's a chance that the index is off if another item is added rapidly and if the list is reordered then ... disaster.
Thanks in advance!
Solved it guys! Very stupid of me to overlook this, but here is the solution:
C# creates a workflowProperties variable for you of type SPWorkflowActivationProperties which contains all the methods and properties to get the data you need:
public SPWorkflowActivationProperties workflowProperties = new SPWorkflowActivationProperties(); // auto generated code.
Then to get the list item data or even list data all you do is:
SPList oList = workflowProperties.List; // get the list that contains the list item on which the workflow instance is running.
SPListItem oItem = workflowProperties.Item; // get the list item on which the workflow instance is running.
So to get my "Name" data I would've had to:
string name = workflowProperties.Item["Name"].ToString();
Hope this helps someone, although fortunately nobody is as stupid as me so you guys will probably figure it out yourself.