I'm developing an outbound call system that shows the user the next company to call with a means to add an appointment for a selected sales rep.
I initially programmed the appointment to be sent in real time as the user saves the current data and move to the next lead. However, there was a significant delay while the appointment was being sent to the email account on Office 365. So I decided to create a small app to send them in batch every 15 minutes.
This works great for the first three appointments, but then I get an error on the 4th appointment:
The Autodiscover service couldn't be located.
I'm suspecting this is something like a hosting company stopping a mailbox from being used for bulk email and there is a limit. Is there something within EWS to allow more than three at a time? Or do I need to amend my code?
Here is my code for sending the appointments:
const string o365Server = "mydomain.co.uk";
var appointmenntList = AppointmentList.GetAppointnmetsToSend();
ExchangeService service = new ExchangeService();
foreach (var appointment in appointmenntList)
{
Console.WriteLine(appointment.IntLeadID);
service.Credentials = new WebCredentials(appointment.StrSalesRepEmail, apointment.StrSalesRepEmailPassword, o365Server);
service.AutodiscoverUrl(appointment.StrSalesRepEmail, RedirectionCallback);
Appointment app = new Appointment(service);
app.Subject = "ASH Waste Appointment with " + appointment.StrLeadAppointmentContact;
app.Body = appointment.StrLeadAppointmentNotes;
app.Start = appointment.DtLeadAppointmentDate;
app.End = app.Start.AddHours(1);
app.Location = appointment.StrLeadAppointmentLocation;
app.RequiredAttendees.Add(appointment.StrSalesRepEmail);
app.ReminderMinutesBeforeStart = 60;
app.Save(SendInvitationsMode.SendOnlyToAll);
Console.WriteLine(appointment);
}
static bool RedirectionCallback(string url)
{
return url.ToLower().StartsWith("https://");
}
I could set the app to do only three at a time and run the app every 2 minutes. Although this would be enough for our sales team, it is limiting the operation.
Any suggestions?
Edit:
Here's the full error:
An unhandled exception of type
Microsoft.Exchange.WebServices.Data.AutodiscoverLocalException'
occurred in Microsoft.Exchange.WebServices.dll
and it occurs on this line of code:
service.AutodiscoverUrl(appointment.StrSalesRepEmail, RedirectionCallback);
I can batch 4 calendar items when using the CreateItems method instead. Have you tried that? Here's some sample code.
public static Collection<ItemId> BatchCreateCalendarItems(ExchangeService service)
{
// These are unsaved local instances of an Appointment object.
// Despite the required parameter of an ExchangeService object (service), no call
// to an Exchange server is made when the objects are instantiated.
// A call to the Exchange server is made when the service.CreateItems() method is called.
Appointment appt1 = new Appointment(service);
Appointment appt2 = new Appointment(service);
Appointment appt3 = new Appointment(service);
Appointment appt4 = new Appointment(service);
// Set the properties for a single instance appointment
appt1.Subject = "Appt1";
appt1.Body = "Appt1";
appt1.Start = DateTime.Now.AddDays(1);
appt1.End = appt1.Start.AddHours(3);
appt1.Location = "My office";
appt1.ReminderMinutesBeforeStart = 30;
// Set the properties for a single instance appointment
appt2.Subject = "Appt2";
appt2.Body = "Appt2";
appt2.Start = DateTime.Now.AddDays(1);
appt2.End = appt1.Start.AddHours(4);
appt2.Location = "My office";
appt2.ReminderMinutesBeforeStart = 30;
// Set the properties for a single instance appointment
appt3.Subject = "Appt3";
appt3.Body = "Appt3";
appt3.Start = DateTime.Now.AddDays(1);
appt3.End = appt1.Start.AddHours(5);
appt3.Location = "My office";
appt3.ReminderMinutesBeforeStart = 30;
// Set the properties for a single instance appointment
appt4.Subject = "Appt4";
appt4.Body = "Appt4";
appt4.Start = DateTime.Now.AddDays(1);
appt4.End = appt1.Start.AddHours(6);
appt4.Location = "My office";
appt4.ReminderMinutesBeforeStart = 30;
// Add the appointment objects to a collection
Collection<Appointment> calendarItems = new Collection<Appointment>() { appt1, appt2, appt3, appt4 };
// Instantiate a collection of item ids to populate from the values that are returned by the Exchange server.
Collection<ItemId> itemIds = new Collection<ItemId>();
// Send the batch of appointment objects.
// Note that multiple calls to the Exchange server may be made when appointment objects have attachments.
// Note also that the item collection passed as the first parameter to CreateItems will have their ids set on return.
ServiceResponseCollection<ServiceResponse> response = service.CreateItems(calendarItems,
WellKnownFolderName.Calendar,
MessageDisposition.SendAndSaveCopy,
SendInvitationsMode.SendToAllAndSaveCopy);
if (response.OverallResult == ServiceResult.Success)
{
Console.WriteLine("All appointments and meetings sucessfully created.");
}
// Collect the item ids from the created calendar items.
foreach (Appointment appt in calendarItems)
{
itemIds.Add(appt.Id);
}
int counter = 1;
// Show the ids and errors for each message
foreach (ServiceResponse resp in response)
{
// Note that since item ids are long, show only 5 characters.
Console.WriteLine("Result (message {0}), id {1}: {2}", counter, itemIds[counter - 1].ToString().Substring(0, 5), resp.Result);
Console.WriteLine("Error Code: {0}", resp.ErrorCode);
Console.WriteLine("ErrorMessage: {0}\r\n", resp.ErrorMessage);
counter++;
}
// Return the collection of item ids
return itemIds;
}
Well, it isn't that you're getting blocked from sending a specific number of appointments, it's that Autodiscover is failing. That error is one that the EWS Managed API likes to return for all kinds of scenarios, so it doesn't tell me for sure why it is failing. It could be a temporary network issue, or it could be that the Autodiscover servers are throttling you. I'm not sure offhand if Autodiscover does any throttling, but it's certainly possible.
To see why it's failing, you could enable tracing with all of the Autodiscover-related trace flags turned on and see what kinds of errors are coming back.
Related
I'm experimenting with the new WinRT Appointments API in Windows 8.1, based on a sample provided on the MSDN website of Microsoft: http://code.msdn.microsoft.com/windowsapps/Appointments-API-sample-2b55c76e
It works great and I can add appointments without a hassle, but there's always a confirmation by the user involved when using the method ShowAddAppointmentAsync from the Windows.ApplicationModel.Appointments.AppointmentManager namespace, which shows the Appointments provider Add Appointment UI.
I'm looking for a solution to add a larger collection of appointments in the default Windows 8 calendar, WITHOUT the confirmation for each individual appointment in the collection. Is there a way to get around this and bulk insert appointments? Maybe the Windows Live SDK?
Its true that the API prompts the user before saving, but there is a provision to achieve this.
var appointment = new Windows.ApplicationModel.Appointments.Appointment();
appointment.details = "This is a dummy appointment";
appointment.reminder = 15000;
appointment.subject = "TEST APPPOINTMENT";
var x = new Windows.ApplicationModel.Appointments.AppointmentManager.requestStoreAsync(Windows.ApplicationModel.Appointments.AppointmentStoreAccessType.appCalendarsReadWrite).done(function (apppointmentStore) {
apppointmentStore.createAppointmentCalendarAsync("TEST CALENDAR").done(function (calendar) {
calendar.saveAppointmentAsync(appointment);
});
})
Here you're an example to do it using C#
private AppointmentCalendar currentAppCalendar;
private AsyncLazy<AppointmentStore> lazyAppointmentStore = new AsyncLazy<AppointmentStore>(async () =>
{
var appStore = await AppointmentManager.RequestStoreAsync(AppointmentStoreAccessType.AppCalendarsReadWrite);
return appStore;
});
private AppointmentStore AppStore { get { return lazyAppointmentStore.Value.Result; } }
public AppointmentService()
{
}
public async Task CreateCalendar()
{
IReadOnlyList<AppointmentCalendar> appCalendars =
await AppStore.FindAppointmentCalendarsAsync(FindAppointmentCalendarsOptions.IncludeHidden);
AppointmentCalendar appCalendar = null;
// Apps can create multiple calendars. Here app creates only one.
if (appCalendars.Count == 0)
{
appCalendar = await AppStore.CreateAppointmentCalendarAsync(Constants.CalendarName);
}
else
{
appCalendar = appCalendars[0];
}
appCalendar.OtherAppReadAccess = AppointmentCalendarOtherAppReadAccess.Full;
appCalendar.OtherAppWriteAccess = AppointmentCalendarOtherAppWriteAccess.SystemOnly;
// This app will show the details for the appointment. Use System to let the system show the details.
appCalendar.SummaryCardView = AppointmentSummaryCardView.App;
await appCalendar.SaveAsync();
currentAppCalendar = appCalendar;
}
public async Task<bool> CreateNewAppointment(Data.Schemas.Task task)
{
if (null == task)
throw new ArgumentNullException("task");
Appointment newAppointment = new Appointment();
this.SaveAppointmentData(task, newAppointment);
try
{
// Show system calendar to the user to be edited
string appointmentId = await AppointmentManager.ShowAddAppointmentAsync(newAppointment, Windows.Foundation.Rect.Empty);
return ! string.IsNullOrWhiteSpace(appointmentId);
// Just save the appointment
// await currentAppCalendar.SaveAppointmentAsync(newAppointment);
// return true;
}
catch
{
return false;
}
}
Check my post, to know more about AsyncLazy.
I hope this help you.
Regards.
Juanlu
This is not possible, by using the WinRT appointments API.
A user interaction is always required. It was a design decision by MS that some actions require user interaction and this is one of it.
As stated by #Ken Tucker, you can use the windows live api to create appointments but this requires the user of your app to sing in to windows live and grat it the required permissions.
I'm implementing an Outlook plugin and I need to create appointments by code, the appointments I want to model occur in rooms, rooms live in Outlook Exchange. How can I add various rooms to the appointment created by code, consider the following code to create an appointment:
Outlook.AppointmentItem newAppointment =
(Outlook.AppointmentItem)
this.Application.CreateItem(Outlook.OlItemType.olAppointmentItem);
newAppointment.Start = DateTime.Now.AddHours(2);
newAppointment.End = DateTime.Now.AddHours(3);
newAppointment.Location = "SOME_LOCATION";
newAppointment.Body =
"We will discuss progress on the group project.";
newAppointment.AllDayEvent = false;
newAppointment.Subject = "Group Project";
newAppointment.Recipients.Add("Roger Harui");
Outlook.Recipients sentTo = newAppointment.Recipients;
Outlook.Recipient sentInvite = null;
sentInvite = sentTo.Add("Holly Holt");
sentInvite.Type = (int)Outlook.OlMeetingRecipientType
.olRequired;
sentInvite = sentTo.Add("David Junca ");
sentInvite.Type = (int)Outlook.OlMeetingRecipientType
.olOptional;
sentTo.ResolveAll();
newAppointment.Save();
newAppointment.Recipients.ResolveAll();
newAppointment.Display(true);
You add a room the same way you add an attendee - pass the name of the room to Recipients.Add and set the Recipient.Type property to olResource (3).
I have successfully added single appointment using this code now i want to add multiple appointment pro grammatically in a single loop.for example i want to add 5 appointment at a time using loop where dates for every appointment is available in a List.
Thanks in advance :)
SaveAppointmentTask saveAppointmentTask = new SaveAppointmentTask();
saveAppointmentTask.StartTime = nearestDate;
saveAppointmentTask.EndTime = nearestDate.AddMinutes(3) ;
saveAppointmentTask.Subject = "Meet Ali"; // appointment subject
saveAppointmentTask.Location = "In Office"; // appointment location
saveAppointmentTask.Details = "Meet Ali to discuss product launch";//appointment details
saveAppointmentTask.IsAllDayEvent = false;
saveAppointmentTask.Reminder = Microsoft.Phone.Tasks.Reminder.FifteenMinutes;
saveAppointmentTask.AppointmentStatus = Microsoft.Phone.UserData.AppointmentStatus.OutOfOffice;
saveAppointmentTask.Show();
use this code as Navigated To event is all the time called when you land up in the page
private SaveAppointmentTask saveAppointmentTask;
private List<int> listMinutes = new List<int>();
// Constructor
public MainPage()
{
InitializeComponent();
for (int i = 0; i < 10; i++) {
listMinutes.Add(i);
}
}
int countAdded = 0;
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if (countAdded < 10)
{
saveAppointmentTask = new SaveAppointmentTask();
saveAppointmentTask.StartTime = DateTime.Now.AddMinutes(listMinutes[countAdded]);
saveAppointmentTask.EndTime = saveAppointmentTask.StartTime.Value.AddMinutes(2);
saveAppointmentTask.Subject = "Meet Ali"; // appointment subject
saveAppointmentTask.Location = "In Office"; // appointment location
saveAppointmentTask.Details = "Meet Ali to discuss product launch";//appointment details
saveAppointmentTask.IsAllDayEvent = false;
saveAppointmentTask.Reminder = Microsoft.Phone.Tasks.Reminder.FifteenMinutes;
saveAppointmentTask.AppointmentStatus = Microsoft.Phone.UserData.AppointmentStatus.OutOfOffice;
countAdded++;
saveAppointmentTask.Show();
}
else {
// do not add anything
}
}
save i that is the count in some application state or a tokes so that you can know if you have added the events :) Appplication.Current.Resources.Add("token", "number added")
Thanks
You cannot automatically save all appointments. You would have to launch the task in each iteration, and user interaction would be nedded in each.
A possible option is to use the Live Connect API. That would allow you to programatically create apointments in user's Live calendar:
Interacting with calendars (Live Connect API)
This way you don't interact with the phone calendar, but the users's Live calendar, which can be synchronized in the phone. Of course, the user will need to authenticate.
I'm using the DDay library to create an iCal event, so that users of my site can add something to their calendar.
I want them to add an appointment as opposed to a meeting request in Office 2010 (and hopefully others too). When I use the library and set the method to PUBLISH, it does appear as an appointment, but it reports that the meeting cannot be found in the calendar. Then when I click no response required, the item gets deleted and doesn't stay in their calendar.
If I change the method to REQUEST, it shows up as a meeting request. This would an okay second best option, but the 'to' field is blank. If that's the best I can do, how can I set the 'to' field? I guess I would have them respond to themselves.
private static string CreateCalendarEvent(
string title, string body, DateTime startDate, double duration,
string location, string organizer, string eventId, bool allDayEvent)
{
// mandatory for outlook 2007
if(String.IsNullOrEmpty(organizer))
throw new Exception("Organizer provided was null");
var iCal = new iCalendar
{
Method = "PUBLISH",
Version = "2.0"
};
// "REQUEST" will update an existing event with the same UID (Unique ID) and a newer time stamp.
//if (updatePreviousEvent)
//{
// iCal.Method = "REQUEST";
//}
var evt = iCal.Create<Event>();
evt.Summary = title;
evt.Start = new iCalDateTime(startDate);
evt.Duration = TimeSpan.FromHours(duration);
evt.Description = body;
evt.Location = location;
evt.IsAllDay = allDayEvent;
evt.UID = String.IsNullOrEmpty(eventId) ? new Guid().ToString() : eventId;
evt.Organizer = new Organizer(organizer);
evt.Alarms.Add(new Alarm
{
Duration = new TimeSpan(0, 15, 0),
Trigger = new Trigger(new TimeSpan(0, 15, 0)),
Action = AlarmAction.Display,
Description = "Reminder"
});
return new iCalendarSerializer().SerializeToString(iCal);
}
When I set the organizer to an email address, as opposed to a test string, it worked fine. I had written all of this up, so I thought I'd share it in case anyone else had the same problem
My app stopped working when the exchange server was upgraded to Outlook 2010 from 2003. Before the upgrade PUBLISH worked fine but now I had to change to REQUEST
Thanks for the article
I'm using the Business Objects Web Services SDK to access our Business Objects data. I've successfully got a list of reports, and from that found the LastSuccessfulInstance of a report that has been previously run. However, I can't seem to get the LastRunTime to be populated. When I do a query with no attributes specified it comes back as not set, and I get the same result when I ask for that attribute in particular. I've looked at the report itself and the instance and they both don't have this information. Does anyone know where I can get it from?
Here's my code (hacked from one of SAP's demos):
var sessConnUrl = serviceUrl + "/session";
var boConnection = new BusinessObjects.DSWS.Connection(sessConnUrl);
var boSession = new Session(boConnection);
// Setup the Enterprise Credentials used to login to the Enterprise System
var boEnterpriseCredential = new EnterpriseCredential
{
Domain = cmsname,
Login = username,
Password = password,
AuthType = authType
};
// Login to the Enterprise System and retrieve the SessionInfo
boSession.Login(boEnterpriseCredential);
/************************** DISPLAY INBOX OBJECTS *************************/
// Retrieve the BIPlatform Service so it can be used to add the USER
var biPlatformUrl = boSession.GetAssociatedServicesURL("BIPlatform");
var boBiPlatform = BIPlatform.GetInstance(boSession, biPlatformUrl[0]);
// Specify the query used to retrieve the inbox objects
// NOTE: Adding a "/" at the end of the query indicates that we want to
// retrieve the all the objects located directly under the inbox.
// Without the "/" Path operator, the inbox itself would be returned.
const string query = "path://InfoObjects/Root Folder/Reports/";
// Execute the query and retrieve the reports objects
var boResponseHolder = boBiPlatform.Get(query, null);
var boInfoObjects = boResponseHolder.InfoObjects.InfoObject;
// If the reports contains a list of objects, loop through and display them
if (boInfoObjects != null)
{
// Go through and display the list of documents
foreach (var boInfoObject in boInfoObjects)
{
var report = boInfoObject as Webi;
if (report == null)
continue;
if (!string.IsNullOrEmpty(report.LastSuccessfulInstanceCUID))
{
var instanceQuery = "cuid://<" + report.LastSuccessfulInstanceCUID + ">";
var instanceResponseHolder = boBiPlatform.Get(instanceQuery, null);
var instance = instanceResponseHolder.InfoObjects.InfoObject[0];
}
}
}
Both report.LastRunTimeSpecified and instance.LastRunTimeSpecified are false and both LastRunTime are 01\01\0001, but I can see a last run time in the Web Intelligence UI.
With a little help from Ted Ueda at SAP support I figured it out. Not all the properties are populated by default you need to append #* to the query string to get everything, i.e. change the line:
const string query = "path://InfoObjects/Root Folder/Reports/";
to:
const string query = "path://InfoObjects/Root Folder/Reports/#*";