Before you close this as a duplicate please look at the other similarly titled question, there is no answer to the problem he just marked it as answered and left.
I am getting this lovely and descriptive error from the EWS manged API whenever I attempt to edit the RequiredAttendees
property on an appointment.
Set action is invalid for property.
Looking at the exception details shows me that it is indeed the RequiredAttendees
property that is causing problems but I have no idea why.
The credentials I use to connect to the service are those of the meeting organizer, I have even tried impersonating the user with no luck. Scratching my head trying to figure out what went wrong here.
Here are the relevant parts of the update routine that are causing problems.
PropertySet props = new PropertySet(
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.Id,
AppointmentSchema.Organizer,
AppointmentSchema.Subject,
AppointmentSchema.Body,
AppointmentSchema.RequiredAttendees);
props.RequestedBodyType = BodyType.Text;
Appointment appointment = Appointment.Bind(_service, new ItemId(appointmentId), props);
if (IsResource(appointment.Organizer.Address) && appointment.Organizer.Address != resourceId)
{
/*
* removed for brevity, no attendee manipulation here
*/
}
else
{
List<Attendee> remove = new List<Attendee>();
foreach (var attendee in appointment.RequiredAttendees)
{
if (IsResource(attendee.Address) && attendee.Address != resourceId)
{
remove.Add(attendee);
}
}
remove.ForEach(a => appointment.RequiredAttendees.Remove(a));
if (!appointment.RequiredAttendees.Any(a => a.Address == resourceId))
{
appointment.RequiredAttendees.Add(resourceId);
}
}
/*
* removed for brevity, no attendee manipulation here
*/
if (IsAvailable(resourceId, startTime, endTime, appointmentId))
appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy);
else
throw new RoomUnavailableException();
Request Trace:
<Trace Tag = "EwsRequest" Tid="14" Time="2017-09-25 20:20:24Z" Version="15.00.0847.030">
<?xml version = "1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<t:RequestServerVersion Version = "Exchange2013" />
</ soap:Header>
<soap:Body>
<m:UpdateItem ConflictResolution = "AlwaysOverwrite" SendMeetingInvitationsOrCancellations="SendToAllAndSaveCopy">
<m:ItemChanges>
<t:ItemChange>
<t:ItemId Id = "AAMkAGEwYWRjZjA3LWNlZjAtNDI2Ny05ZjQwLWUzYWZjOThhMjkzNwBGAAAAAABWdX+yf6THTpO/1LYpoG6xBwD6lEwS6u8XQbDhIlTh/X/UAAAAAAENAAD6lEwS6u8XQbDhIlTh/X/UAAAi3oSdAAA=" ChangeKey="DwAAABYAAAD6lEwS6u8XQbDhIlTh/X/UAAAi3ocU" />
<t:Updates>
<t:SetItemField>
<t:FieldURI FieldURI = "calendar:RequiredAttendees" />
< t:CalendarItem>
<t:RequiredAttendees>
<t:Attendee>
<t:Mailbox>
<t:Name>Exchange Test</t:Name>
<t:EmailAddress>etest @supertester.com</t:EmailAddress>
<t:RoutingType>SMTP</t:RoutingType>
<t:MailboxType>Mailbox</t:MailboxType>
</t:Mailbox>
</t:Attendee>
<t:Attendee>
<t:Mailbox>
<t:EmailAddress>redroom @supertester.com</t:EmailAddress>
</t:Mailbox>
</t:Attendee>
</t:RequiredAttendees>
</t:CalendarItem>
</t:SetItemField>
</t:Updates>
</t:ItemChange>
</m:ItemChanges>
</m:UpdateItem>
</soap:Body>
</soap:Envelope>
</Trace>
Response Trace:
<Trace Tag = "EwsResponse" Tid="14" Time="2017-09-25 20:20:24Z" Version="15.00.0847.030">
<?xml version = "1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion = "15" MinorVersion="1" MajorBuildNumber="225" MinorBuildNumber="41" Version="V2_48" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<m:UpdateItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:UpdateItemResponseMessage ResponseClass = "Error" >
<m:MessageText>Set action is invalid for property.</m:MessageText>
<m:ResponseCode>ErrorInvalidPropertySet</m:ResponseCode>
<m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
<m:MessageXml>
<t:FieldURI FieldURI = "calendar:RequiredAttendees" />
</m:MessageXml>
<m:Items />
</m:UpdateItemResponseMessage>
</m:ResponseMessages>
</m:UpdateItemResponse>
</s:Body>
</s:Envelope>
</Trace>
Big thanks to Glen Scales for pointing me in the right direction with this one.
When I was retrieving appointments I was using the following code:
CalendarFolder calendar = CalendarFolder.Bind(_service, new FolderId(WellKnownFolderName.Calendar, resourceId), PropertySet.IdOnly);
CalendarView cView = new CalendarView(startDate, endDate, _maxAppointments);
cView.PropertySet = new PropertySet(PropertySet.IdOnly);
FindItemsResults<Appointment> appointments = calendar.FindAppointments(cView);
Where resourceId
was the address of the room mailbox, not the organizer of the meeting.
Editing the attendees on an appointment that is not the original is not allowed so that is what was giving me the error. In order to update the attendees I have to retrieve the organizers appointment with the following code (borrowed heavily from this post):
appointment = FindOrganizerAppointment(appointment);
/// <summary>
/// Finds the related Appointment.
/// </summary>
/// <param name="appointment">The appointment whose original is to be found.</param>
/// <returns></returns>
private Appointment FindOrganizerAppointment(Appointment appointment)
{
try
{
Impersonate(appointment.Organizer.Address);
var filter = new SearchFilter.IsEqualTo
{
PropertyDefinition = new ExtendedPropertyDefinition
(DefaultExtendedPropertySet.Meeting, 0x03, MapiPropertyType.Binary),
Value = GetObjectIdStringFromUid(appointment.ICalUid)
};
var view = new ItemView(1) { PropertySet = new PropertySet(BasePropertySet.FirstClassProperties) };
return _service.FindItems(WellKnownFolderName.Calendar, filter, view).Items[0] as Appointment;
}
catch (Exception e)
{
throw e;
}
finally
{
DisableImpersonation();
}
}
/// <summary>
/// Gets the object id string from uid.
/// <remarks>The UID is formatted as a hex-string and the GlobalObjectId is displayed as a Base64 string.</remarks>
/// </summary>
/// <param name="id">The uid.</param>
/// <returns></returns>
private static string GetObjectIdStringFromUid(string id)
{
var buffer = new byte[id.Length / 2];
for (int i = 0; i < id.Length / 2; i++)
{
var hexValue = byte.Parse(id.Substring(i * 2, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
buffer[i] = hexValue;
}
return Convert.ToBase64String(buffer);
}
Please do note that "RequiredAttendees" object will be filled only if it a meeting.
In your else statement add this check
if(appointment.IsMeeting)
{
List<Attendee> remove = new List<Attendee>();
foreach (var attendee in appointment.RequiredAttendees)
{
if (IsResource(attendee.Address) && attendee.Address != resourceId)
{
remove.Add(attendee);
}
}
remove.ForEach(a => appointment.RequiredAttendees.Remove(a));
if (!appointment.RequiredAttendees.Any(a => a.Address == resourceId))
{
appointment.RequiredAttendees.Add(resourceId);
}
}
info about appointment meeting property MSDN link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With