EWS: Search in email body only responses 250 results - c#

I currently got a problem with the exchange web service in C#.
I'm trying to loop through mails that are older than 3 months, but I only get 250 mails back. In the folder are more than 80.000 mails so that isn't the correct count.
After searching for the problem I now know that the service only gives me 250 results per page. But the TotalCount is 250 too, so I can't work with paging because there is no second page. Without the body filter it works and I get back like 70.000 mails which is correct. Has anyone had the same problem and can help me?
Currently the programm just uses 2 filters with the SearchFilter And operator and orders the result ascending.
Here is my current code for getting the mails:
Folder folder; // folder from loop
DateTime dateStart; // = today - 3 months
SearchFilter.SearchFilterCollection filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And);
filter.Add(new SearchFilter.IsLessThanOrEqualTo(EmailMessageSchema.DateTimeReceived, dateStart));
filter.Add(new SearchFilter.ContainsSubstring(ItemSchema.Body, "test"));
ItemView view = new ItemView(1000, 0, OffsetBasePoint.Beginning);
// Get oldest first
view.PropertySet = new PropertySet(BasePropertySet.IdOnly, ItemSchema.DateTimeReceived);
view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Ascending);
view.Traversal = ItemTraversal.Shallow;
FindItemsResults<Item> findResults = exchangeService.FindItems(folder.Id, filter, view);
findResults.TotalCount() // = 250
I would be glad about a solution, thanks for helping :)

Am sure you have figured this out by now, but it is due to EWS throttling. While this can be changed, it is not advised. Only workaround I have found is to move the items to another folder and keep re-running logic. Here is a link to info on EWS throttling and also a way to determine your current policy - https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/ews-throttling-in-exchange.

Related

Google Sheet API - BatchUpdate function help needed in C#

I need some help in the batchupdate function for Google Sheet api. Currently in my project I am doing some processing and call my createEntry function to add a MSG to a specific cell on the gsheet using the append function as shown below. I realised every time I call that it adds to my quota of API calls requests and Google api stops and hence my program.
To workaround I thought I would store my data in 2D array and update all oneshot towards the end using batchUpdate to avoid exceeding the number of user requests per 100secs and per user set by Google API limits.
My questions are:
Is my logic correct in using batchupdate to solve the user request to Google API problem (keeping in mind I might be adding thousands of data across 6 columns later)?
PS: I have tried already putting the thread to sleep for 20 secs after every 20 or so inserts, but that is not solving the problem as it is unpredictable. sometimes I can go up to 400 cells of data and sometimes it will stop at just 70.
if yes, how can I batchupdate insert various data points in a range of cells?
My code I am using currently is-
public void CreateEntry(string col,int ctw, string msg)
{
var range = $"{sheet}!" + col.ToString() + ctw.ToString();
var valueRange = new ValueRange();
var oblist = new List<object>() { };
oblist.Add(msg);
valueRange.Values = new List<IList<object>> { oblist };
var appendRequest = service.Spreadsheets.Values.Append(valueRange, SpreadsheetId, range);
appendRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
var appendReponse = appendRequest.Execute();
}
A simple sample of the gsheet data points for reference-
As of right now as you can see from my code above i am writing each cell individually. I want to now add all oneshot to avoid exceeding number of user requests per 100sec limit by Google.

How does one send an exchange appointment invite to only one of the required attendees programmatically with C#?

I only want the room's email to receive an invitation, not the other requiredAttendees. However, the "Save()" method appears to only have a send to all or none type of setup. Does anyone have a solution?
appointment.Subject = myAppointmentInfo.ClassName;
appointment.Body = "";
appointment.Start = myAppointmentInfo.StartDateTime;
appointment.End = myAppointmentInfo.EndDateTime;
appointment.Location = myAppointmentInfo.Location;
appointment.ReminderMinutesBeforeStart = 5;
appointment.RequiredAttendees.Add(employeeEmail1);
appointment.RequiredAttendees.Add(employeeEmail2);
appointment.RequiredAttendees.Add(roomEmail);
appointment.Save(SendInvitationsMode.SendToNone);
I have a partial solution to this now. What I ended up doing was this:
var calendar = Folder.Bind(service, new FolderId(WellKnownFolderName.Calendar, roomEmail));
appointment.Save(calendar.Id, SendInvitationsMode.SendToNone);
It allowed me to send to only the roomEmail, but still have the other required attendees show up on the list of Required Attendees.
This solved my initial problem, but now I need to learn how to do something similar for my appointment updates and deletes (which is proving to be more difficult). If you know how to do these things please let me know! Thanks
UPDATE: I found my issue with this problem as well. To do updates and deletes without sending notifications to the attendees, use:
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToNone);
for updates, and:
appointment.Delete(DeleteMode.SoftDelete, SendCancellationsMode.SendToNone);
my biggest issue before was that I was setting the DeleteMode type to "MoveToDeletedItems" instead of "SoftDelete".

GetMessage() take excessive long time MailKit

When I call
var result = MailKit.GetMessage(uid)
Then the call takes around 4 seconds to return. Is there any better/faster way to get the mime message from the server??
Currently doing the following:
1. Fetch(0, -1, MessageSummaryItems.UniqueId | MessageSummaryItems.Envelope | MessageSummaryItems.Flags)
2. Loop the fetch result to find a messageId match
3. Send GetMessage() with the UID found previously.
I can see that FETCH in most cases takes around 2 secs, all in all my process will take something like 6 seconds, which is kinda "long" before I can present the email in the client.
Is there a faster way to get a mime message from a mail server using MailKit, when all I know about the email is the MessageID??
You could cache all of the message summaries so that you don't have to ask the server for them each time...
You could also try:
var uids = folder.Search (SearchQuery.HeaderContains ("Message-Id", messageId));
if (uids.Count > 0)
message = folder.GetMessage (uids[0]);

Trouble getting GetMeaningfulUseVDTReport results with HealthVault SDK

I have been working on an application that sends DOPU (drop-off/pick-up) requests for CCD documents via Health. Creating the DOPU requests and getting the corresponding token generated by HealthVault work fine.
There are two SDK methods I am using to get Meaningful Use report data right now:
OfflineWebApplicationConnection.GetMeaningfulUseTimelyAccessDOPUDocumentReport gets me all the DPU requests sent. This works fine, this always gives me the correct DOPU requests (with data/time stamp, token, and application ID).
The other is OfflineWebApplicationConnection.GetMeaningfulUseVDTReport method. This is the one causing problems. No matter what date range I set (a week, a month, Datetime.MinValue to DateTime.MaxValue), I always get no results. No matter how many time I go into my HV account, to view and download my connection DOPU documents. That SDK method still gives me no results.
I have also tried using CCD extension XML when sending a CCD to specifically set the patient-id and entry-date. Again, this doesn't affect my report results.
Does anyone else with more experience than I have with the Meaningful User methods in the SDK that I, have any suggestions on why I get nothing at all, ever, for the OfflineWebApplicationConnection.GetMeaningfulUseVDTReport call?
Here is some sample code that I am using to run the reports (some of the commented lines are just me trying different date ranges). I can also post snippets of code showing how I am sending the DOPU requests, even though that all seems to be behaving as expected.
class Program
{
static void Main(string[] args)
{
var applicationId = ConfigurationManager.AppSettings["ApplicationId"];
var url = ConfigurationManager.AppSettings["HealthServiceUrl"];
var connection = new OfflineWebApplicationConnection(new Guid(applicationId), url, Guid.Empty/* offlinePersonId */);
Console.WriteLine("\nGetMeaningfulUseTimelyAccessDOPUDocumentReport");
//var receipts = connection.GetMeaningfulUseTimelyAccessDOPUDocumentReport(new DateRange(new DateTime(2014, 11, 19), new DateTime(2014, 12, 19)));
var receipts = connection.GetMeaningfulUseTimelyAccessDOPUDocumentReport(new DateRange(DateTime.MinValue, DateTime.MaxValue));
//var receipts = connection.GetMeaningfulUseTimelyAccessDOPUDocumentReport(new DateRange(DateTime.UtcNow.AddMonths(-12), DateTime.UtcNow));
foreach (var receipt in receipts)
{
Console.WriteLine(string.Format("{0} - {1} - {2}", receipt.AvailableDate, receipt.PackageId, receipt.Source));
}
Console.WriteLine("\nGetMeaningfulUseVDTReport");
//var activities = connection.GetMeaningfulUseVDTReport(new DateRange(new DateTime(2000, 12, 3), new DateTime(2014, 12, 10)));
//var activities = connection.GetMeaningfulUseVDTReport(new DateRange(DateTime.MinValue, DateTime.MaxValue));
var activities = connection.GetMeaningfulUseVDTReport(new DateRange(DateTime.UtcNow.AddMonths(-12), DateTime.UtcNow.AddDays(1)));
foreach (var activity in activities)
{
Console.WriteLine(activity.PatientId);
}
Console.ReadLine();
}
}
Update 1
Tried the sample Meaningful Use web application that MS had on codeplex. Used it with our application ID/credentials. Well, it worked. Not sure what is different, at least, so far.
Update 2
So I have tried many other real CCDs (in our PPE enrionment, immediately deleting them when done) including test CCDs. I even set up the ConnectPackage in my app to behave the same as the test application from MS. No matter what I send, I get know Meaningful Use VDT data for the CCDs. The test CCD in the MS test application, however, works.
Update 3
Tried sending CCDs through the MS test application. Again, it sends and I can connect to an HV account with no problem. I get no VDT data, no matter the date range used. Maybe there is an issue with our CCDs?

Fetching all mails in Inbox from Exchange Web Services Managed API and storing them as a .eml files

I want to fetch all mails in the Inbox folder using EWS Managed API and store them as .eml. The problem is in fetching (1) all mails with (2) all headers (like from, to, subject) (I am keeping information of those values of from, to and other properties somewhere else, so I need them too) and (3)byte[] EmailMessage.MimeContent.Content. Actually I am lacking understanding of
Microsoft.Exchange.WebServices.Data.ItemView,
Microsoft.Exchange.WebServices.Data.BasePropertySet and
Microsoft.Exchange.WebServices.Data.ItemSchema
thats why I am finding it difficult.
My primary code is:
When I create PropertySet as follows:
PropertySet properties = new PropertySet(BasePropertySet.FirstClassProperties, ItemSchema.MimeContent);
I get following exception:
The property MimeContent can't be used in FindItem requests.
I dont understand
(1) What these ItemSchema and BasePropertySet are
(2) And how we are supposed to use them
So I removed ItemSchema.MimeContent:
PropertySet properties = new PropertySet(BasePropertySet.FirstClassProperties);
I wrote simple following code to get all mails in inbox:
ItemView view = new ItemView(50);
view.PropertySet = properties;
FindItemsResults<Item> findResults;
List<EmailMessage> emails = new List<EmailMessage>();
do
{
findResults = service.FindItems(WellKnownFolderName.Inbox, view);
foreach (var item in findResults.Items)
{
emails.Add((EmailMessage)item);
}
Console.WriteLine("Loop");
view.Offset = 50;
}
while (findResults.MoreAvailable);
Above I kept page size of ItemView to 50, to retrieve no more than 50 mails at a time, and then offsetting it by 50 to get next 50 mails if there are any. However it goes in infinite loop and continuously prints Loop on console. So I must be understanding pagesize and offset wrong. I want to understand
(3) what pagesize, offset and offsetbasepoint in ItemView constructor means
(4) how they behave and
(5) how to use them to retrieve all mails in the inbox
I didnt found any article online nicely explaining these but just giving code samples. Will appreciate question-wise explanation despite it may turn long.
EWS is a bit inconsistent with the properties returned from various operations. An Item.Bind will not return the exact same properties as a FindItem. You're using PropertySets properly as far as defining what you want from the server, but you have to use them in the right place. What you need to do is find the items, then load the properties into them. It's not ideal, but that's the way EWS works. With your loop, you're constantly assigning 50 to your offset when you need to increment it by 50. Off the top of my head, something like this would do:
int offset = 0;
int pageSize = 50;
bool more = true;
ItemView view = new ItemView(pageSize, offset, OffsetBasePoint.Beginning);
view.PropertySet = PropertySet.IdOnly;
FindItemsResults<Item> findResults;
List<EmailMessage> emails = new List<EmailMessage>();
while(more){
findResults = service.FindItems(WellKnownFolderName.Inbox, view);
foreach (var item in findResults.Items){
emails.Add((EmailMessage)item);
}
more = findResults.MoreAvailable;
if (more){
view.Offset += pageSize;
}
}
PropertySet properties = (BasePropertySet.FirstClassProperties); //A PropertySet with the explicit properties you want goes here
service.LoadPropertiesForItems(emails, properties);
Now you have all of the items with all of the properties that you requested. FindItems often doesn't return all of the properties you want even if you ask for them, so loading only the Id initially and then loading up the properties you want is generally the way to go. You may also want to batch the loading of properties in some way depending on how many emails you're retrieving, perhaps in the loop prior to adding them to the List of EmailMessages. You could also consider other methods of getting items, such as a service.SyncFolder action.

Categories