I'm new to EWS so forgive me if this is obvious. I searched and could not find a solution.
I have a command line C# utility that filters emails and organizes them into folders. The app seems to work fine logically, but appears to be timing out after 15 minutes without error.
The utility connects using autodiscover:
private ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
...
// Connect using credentials and autodiscover
service.Credentials = new WebCredentials(userName, password, domain);
service.AutodiscoverUrl(emailServer);
Then after filtering, it loops through the results and moves the emails into the appropriate folders. Something like this:
...
// Create a filter to only return unread messages
SearchFilter emailFilter = new SearchFilter.IsLessThan(EmailMessageSchema.DateTimeReceived, DateTime.Now.AddDays(-2));
// Retrieve the results
FindItemsResults<Item> findResults = service.FindItems(folder, emailFilter, view);
Logger.Write("Total emails to be archived: " + findResults.TotalCount);
...
try
{
...
foreach (Item email in findResults)
{
EmailMessage emailMessage = EmailMessage.Bind(service, email.Id);
emailMessage.Load();
emailMessage.Move(folder5.Folders[0].Id);
}
}
catch (Exception ex)
{
Logger.Write("Exception caught while moving emails: " + ex.Message);
throw ex;
}
...
Logger.Write("=== End Email Move Logic...");
My first logging output indicates that 5,000 emails should be moved, and in fact, the process begins nicely, but after 15 minutes, I see the final logging output. Indicating that everything completed. Yet, only a portion of the total 5000 get moved. Oddly, I do not see an exception or anything in the event logs.
EDIT: 7/9/2014
Ok, my initial problem was due to the fact that I did not notice that the FindItemsResults class uses paging! Doh! So even though it correctly reported the TotalCount (5000), when I iterated through the results, only 1000 were being processed. Not terribly intuitive to a EWS noob like me, but there you have it.
Unfortunately, I have a follow-up frustration. Based on Brad's comments below, I'm now using the ExchangeService.MoveItems method. After dealing with the paging issue, I now have a proper list of itemIds (count = 5000) that I pass to the MoveItems method. Like this:
service.MoveItems(itemIds, folder5.Folders[0].Id);
To my surprise, only 730 emails were moved. Leaving 4270 emails unmoved. So, hoping to find a pattern, I ran it again, this time 671 emails were moved. No pattern.
Any input would be greatly appreciated.
I appreciate this is an old thread but it was the first I found on Google so thought I would add my findings.
I coded a WPF app to help in migrating legacy Exchange public folders to Office 365 hosted Exchange and have been experiencing timeouts when searching using the FindItems method. Specifically I was searching over a date range for date sent in a public folder and then copying the items found to the Office 365 public folder. If we used a large date range then the FindItems call would timeout.
What has fixed it for us is to set the Timeout value for the ExchangeService, the default is 100000 milliseconds, so we just added another 0 to test the possible solution. Hey presto we were able to use much larger date ranges.
ExchangeService service = new ExchangeService();
ExchangeService o365 = new ExchangeService();
switch (strArgs[6])
{
case "0":
service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
break;
case "1":
service = new ExchangeService(ExchangeVersion.Exchange2010);
break;
case "2":
service = new ExchangeService(ExchangeVersion.Exchange2010_SP1);
break;
case "3":
service = new ExchangeService(ExchangeVersion.Exchange2010_SP2);
break;
case "4":
service = new ExchangeService(ExchangeVersion.Exchange2013);
break;
case "5":
service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
break;
}
int nCount = 0;
// Connect to on-premises Exchange Server
callback(nCount, "Connecting to on-premises Exchange Server, please wait....", 0, true, mailItem);
service.Credentials = new WebCredentials(strArgs[0], strArgs[1]);
if (!useUrl)
service.AutodiscoverUrl(strArgs[2], RedirectionUrlValidationCallback);
else
service.Url = new Uri(strArgs[10] + "/EWS/Exchange.asmx");
service.Timeout = 1000000;
callback(nCount, "Connected to on-premises Exchange Server, please wait....", 0, true, mailItem);
// Connect to Office 365 Exchange Server
callback(nCount, "Connecting to Office 365 Exchange Server, please wait....", 0, true, mailItem);
o365.Credentials = new WebCredentials(strArgs[7], strArgs[8]);
o365.AutodiscoverUrl(strArgs[9], RedirectionUrlValidationCallback);
callback(nCount, "Connected to Office 365 Exchange Server, please wait....", 0, true, mailItem);
We are using a paging value of 500.
The source Exchange version is Exchange 2007 SP1.
Hope it helps someone.
Timeouts are normal for search queries, especially for large folders.
What happens is Exchange does not keep live indexes to respond to just any search query you can build, so it times out the response and keeps indexing.
If you try to issue the same query later it will succeed. Unfortunately Exchange does not provide information on when, just keep trying. After first page is returned further page queries will return almost immediately.
BTW this is not a throttling issue, you will get a clear error back when throttling limit is reached. Exchange 2007 just says that, 2010 and higher provides you a value of how much to wait until throttling quota will be recharged.
Related
I am using C# with the Exchange Web Service (EWS).
I have meetings that I need to forward and whenever I do they get stripped, but only from some accounts and not others. The link for the meeting is still there but it is not being recognised by the online Outlook as a meeting item, nor by Teams which is connected to the account.
This even happens if I manually forward, but again only if I forward emails that are from some accounts - some other accounts are fine!
I'm using this on incoming emails:
var fwdEmailArr = new EmailAddress[1];
fwdEmailArr [0] = fwdEmail;
MeetingRequest appointment = MeetingRequest.Bind(service, email.Id);
appointment.Forward("", fwdEmailArr);
This is the same issue if I use the email.forward as well, etc.
However, if I create a new appointment and send it, it doesn't get stripped - this is with the same addresses.
Appointment appt = new Appointment(service);
appt.Subject = email.Subject;
appt.Body = appointment.Body;
appt.Start = appointment.Start;
appt.End = appointment.End;
appt.Location = appointment.Location;
appt.RequiredAttendees.Add(fwdEmail);
foreach (var reqAtt in appt.RequiredAttendees)
{
appt.RequiredAttendees.Add(reqAtt);
}
foreach (var reqAtt in appt.OptionalAttendees)
{
appt.OptionalAttendees.Add(reqAtt);
}
appt.RequiredAttendees.Add(appointment.From.Address);
appt.Save(SendInvitationsMode.SendToAllAndSaveCopy);
So, I could do this but it means that they are no longer the same meeting and declining the original wont decline this. Unless there's a way I can connect the meetings or something?
Any ideas how I can stop the meeting being stripped?
Or alternatively just add another recipient to the current meeting, that will show on their calendar?
If anyone comes here with a similar issue, it turns out that, first of all you need to make sure you define the correct server version on the service declaration:
service = new ExchangeService(ExchangeVersion.Exchange2016){}
In addition, for some reason some images when attached to the forwarded email for some reason confuse EWS and make it think there's no meeting. I got around this by scanning the MIME content and just extracting the calendar block and deleting all other attachments.
This has been working flawlessly for about 5 months.
I can't show any code here yet, because i'm still analyzing if it is possible to realize this.
In our company we have a virtual system to make and manage leave applications. I should now check if it would be possible to enter an approved vacation in the Outlook calendar of the applicant.
I would need a central solution which remotely accesses the calendar and enters the appointments. We currently use the on premise solution of Mircosoft Exchange 2019 and Office 365.
During my research I came across EWS but it seems that Exchange 2019 does not support it anymore. Is there possibly another solution which I could use? Basically I would like to realize a solution with C# but I would also be able to realize a Powershell or Java solution. But most of the time I did not find a real solution.
Most of the time the examples are always local on the machines or using an older Exchange Server like 2013. I haven't found reliable information for 2019 yet. I hope someone here can help me or give me a hint. Or it would also be helpful to say if it is not possible.
Best regards!
I am currently working on a solution. I will post the code when I am successfull!**
Exchange 2019 fully supports EWS.
EWS is still the preferred API to access Exchange, even if Microsoft is not adding any new features to it.
ON the client side, you can use Outlook Object Model and its Namespace.GetSharedDefaultFolder methods. Once you have an instance of the MAPIFolder object, you can use MAPIFolder.Items.Add to create a new appointment.
I did choose a little bit different approach but I was able to make it fully work. I now can create events and delete them if needed.
public void UpdateCalender()
{
ExchangeService Service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
Uri Url = new Uri("https://localmaildomain.sys/EWS/Exchange.asmx");
Service.Url = Url;
Service.Credentials = new NetworkCredential("service_user","service_password");
Folder inboxFolder = Folder.Bind(Service, new FolderId(WellKnownFolderName.Calendar, Temp.UserMail));
foreach (var entry in Temp.Positions)
{
if (!entry.Storno)
{
try
{
Appointment appointment = new Appointment(Service);
appointment.Subject = $"Urlaub/Vacation ({entry.Type})";
appointment.Body = $"{entry.Comment}";
appointment.IsAllDayEvent = true;
appointment.Start = entry.Date.AddSeconds(1);
appointment.End = entry.Date.AddSeconds(1);
appointment.LegacyFreeBusyStatus = LegacyFreeBusyStatus.OOF;
appointment.Save(inboxFolder.Id, SendInvitationsMode.SendToNone);
}
catch (Exception Ex)
{
Console.WriteLine($"Calender item could not be created! Exception: {Ex.ToString()}");
}
}
else
{
CalendarView view = new CalendarView(entry.Date, entry.Date);
FindItemsResults<Appointment> results = Service.FindAppointments(inboxFolder.Id,view);
foreach (var appointment in results)
{
if (appointment.Subject == $"Urlaub/Vacation ({entry.Type})" && appointment.Start == entry.Date)
{
try
{
appointment.Delete(DeleteMode.MoveToDeletedItems);
}
catch(Exception Ex)
{
Console.WriteLine($"Calender item could not be deleted! Exception: {Ex.ToString()}");
}
break;
}
}
}
}
}
In order to avoid the Exchange Server limit RCAMaxConcurrency (0-100), I want to code a listener service (streamlistener) that can handle up to 5000 users using just one connection. I already have 200 test accounts and another account having impersonate rights over those 200 test accounts.
If possible it would be nice to avoid switching around all accounts.
We already have the code and it works fine doing impersonation just to 1 user.
public void SuscribeToCalendar()
{
// Set the email address of the account to get the appointment.
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.PrincipalName, "xxxxxxxxx");
// Subscribe to streaming notifications in the Inbox.
StreamingSubscription streamingSubscription = service.SubscribeToStreamingNotifications(
new FolderId[] { WellKnownFolderName.Calendar }, EventType.Created, EventType.Modified, EventType.Moved);
// Create a streaming connection to the service object, over which events are returned to the client.
// Keep the streaming connection open for 30 minutes.
StreamingSubscriptionConnection connection = new StreamingSubscriptionConnection(service, 30);
connection.AddSubscription(streamingSubscription);
connection.OnNotificationEvent += OnNotificationEvent;
connection.OnDisconnect += OnDisconnect;
connection.Open();
}
RCAMaxConcurrency doesn't affect EWS it affect Outlook connections which use RPC to connect. What will affect EWS is EWSMaxConcurrency which has a much a lower value to 10 by default. (you would also get affected by EWSMaxSubscriptions which is 20).
The impersonation setting affect the header of the EWS request so you can't impersonate any more the one user per call so when creating a subscription you need one call per user you subscribing. You can group the Subscripting together into one connection with impersonation using the following https://msdn.microsoft.com/en-us/library/office/dn458789(v=exchg.150).aspx .
The limitations with group is 200 users per group, given the amount of churn you have with subscriptions you don't really want any more the this. When your using impersonation the number of connections isn't an issue as long as you don't anchor ever group with the same mailbox.
Cheers
Glen
I am analyzing a users Exchange mailbox with calls to the ExchangeService. This tool needs to run on the client environment periodically and by ommiting the credentials to the service I am connecting to the Exchange Service as the logged in Windows User. I can succesfully loop thrue the folders and items.
Now I want tot retrieve the information about the mailbox being used. Username and (main) E-mail should suffice. But I cannot find anything about how to retrieve this information. Every example provides credentails for the user, or auto-discovering the Exchange service from the e-mail adres. I do not want the user to configure anything :-).
Any suggestions?
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri("https://FQDN/EWS/Exchange.asmx");
???
FindItemsResults<Item> findResults = service.FindItems(WellKnownFolderName.SentItems, new ItemView(100)); // this works
I've tried using service.ResolveName, but that can give multiple answers, even using Environment.UserName
The easiest method to do this is to use ConvertId operation and use unresolvable address (blah#blah.com always works for me) in the Mailbox element. Exchange should convert this to the actual Mailbox in the response. eg
Folder chk = Folder.Bind(service, WellKnownFolderName.Inbox);
AlternateId aiItem = new AlternateId();
aiItem.Mailbox = "Blah#Blah.com";
aiItem.UniqueId = chk.Id.UniqueId;
aiItem.Format = IdFormat.EwsId;
String CasServer = service.Url.Host.ToString();
AlternateIdBase caid = service.ConvertId(aiItem, IdFormat.HexEntryId);
Console.WriteLine(((AlternateId)caid).Mailbox);
Cheers
Glen
I am using EWS api for email operations.
When initially user sets up his account, i just need to collect latest syncstate of his account. My CRON job checks for latest changes from then onwards at regular intervals.
Here is the code
var service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Url = new Uri("https://" + data.ServerAddress + "/ews/Exchange.asmx");
service.Credentials = new NetworkCredential(data.EMail, data.Password);
ServicePointManager.ServerCertificateValidationCallback = (xyzxyz, certificate, chain, sslPolicyErrors) => true;
ChangeCollection<ItemChange> icc = service.SyncFolderItems(new FolderId(WellKnownFolderName.Inbox),
PropertySet.FirstClassProperties,
null,
512,
SyncFolderItemsScope.NormalItems,
null );
return icc.SyncState;
Issue lies in collecting SyncState when user initially sets up his account.
Assume user has 600 email in Inbox. When I request for syncstate with above code, I get syncstate which is valid for first 512 emails. I will have to make one more call to get next syncstate which is valid and final. This will increase waiting time for the user to setup his account. If user has 10000 mails in Inbox, it would be required to make 20 calls to EWS to get final SyncState.
How to get final or latest SyncState with a single call to EWS?
You can't get the latest sync state in a single call, see EWS. How to get latest SyncState without initial synchronization?. Note that starting with Exchange 2010 SP2, EWS returns items from newest to oldest, so the user will get their newest items first - they won't have to wait all 20 calls.