Appointment from ExchangeService: how to access associated contacts? - c#

It's about Outlook 2003, Exchange 2010 (SP2) and the EWS managed API in C#.
I'm trying to get all appointments from the Exchangeserver, this works fine:
ExchangeService service = exchangeService.Service;
CalendarFolder calendarfolder = CalendarFolder.Bind(service, WellKnownFolderName.Calendar);
ItemView view = new ItemView(calendarfolder.TotalCount);
if (calendarfolder.TotalCount > 0)
{
FindItemsResults<Item> calendarItems = service.FindItems(WellKnownFolderName.Calendar, view);
foreach (Item item in calendarItems)
{
if (item is Appointment)
{
//...do something
}
}
}
The first line returns an ExchangeService with all required preferences.
The only problem is this one: I can't get the associated contacts of the appointment. I really need them - not the invited users, only the associated contacts.
Every other attribute is available, except this one...
Please, anyone an idea? I'm working on this since months, and I suspect I already know almost every single article about the EWS managed API on Google without any success on this point.
Thanks in advance.

Related

How to stop meeting being stripped on forward (EWS)

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.

Create appointments in Outlook calenders of employees using C# and Exchange 2019

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;
}
}
}
}
}

EWS: Exchange Items not available right after Update

I'm working on a project to sync calendar items from specific Exchange users to another application and back. In the other application there are meetings, too. My problem in several parts of my project is that I want to add attendees to the appointment, then get the appointment with the credentials of the attendee and accept it. But when I want to check bind the new appointment immediatly after updating the appointment there is no Item with the iCalUid in the folder. When I wait 2000ms after updating the meeting request is there but I don't think this is best practice. I have a short code sample:
string attendeeAddress = "mymailaddress";
ExchangeService service =
new ExchangeService(ExchangeVersion.Exchange2013)
{
Url = new Uri("https://exchange.sample.de/EWS/Exchange.asmx"),
Credentials = new WebCredentials("username", "password", "domain")
};
Appointment app = Appointment.Bind(service, id);
app.RequiredAttendees.Add(attendeeAddress);
app.IsResponseRequested = false;
app.Update(ConflictResolutionMode.AutoResolve);
CalendarView view = new CalendarView(app.Start, app.End);
Thread.Sleep(2000);
FindItemsResults<Appointment> attendeeApps =
serviceAttendee.FindAppointments(WellKnownFolderName.Calendar, view);
Appointment appAttendee;
foreach (Appointment a in attendeeApps)
{
if (a.ICalUid.Equals(app.ICalUid))
{
appAttendee = Appointment.Bind(serviceAttendee, a.Id);
}
}
appAttendee.Accept(false);
From the looks of what your doing your adding an attendee to a meeting and then connecting to the attendees Mailbox to accept that meeting ? If that's the case on the backend this is going to produce a Meeting Invitation that will need to be sent via Email and routed to the Attendees Mailbox via the Hub Transport Role. Even if all the recipients and server roles are on the same server you will need to allow time for the message to be delivered to the Attendees Mailbox this is just the normal way Exchange handles Meeting for local (or remote attendees) it will never be instant as Exchange stores each copy of the Appointment as a separate store Item.

Exchange Web Service (EWS) FindItems does not work if contact is in GAL

I created a small application that gets Contact objects from an external source. Then, depending on some configurations, I have to create/upadate these contacts in a user's contact folder on our exchange server, so that the next time this user opens its MS Outlook, he sees the new contacts (on the exchange server, I have a user with impersonation capabilities, so security is not a concern).
For that, I use the FindItems(folderId, filter, view) method of the EWS library that works good. For the filter, I'm using the user's email address which is quite a good key... If I get a result back, this simply means that the Contact already exist, and that I need to do an update instead of a create. Everything works as expected until here...
BUT, I encounter a problem when the Contact (email address in fact) is already existing in the GAL (Global Address List). In this case, the FindItems method returns no result even if the Contact exists in the folder! It seems (this is a supposition) that the exchange server creates a link for contacts which have an email address that already exist in the GAL and not a new contact. And this could explain why the FindItems method does not return anything in this case. The strange thing is that if I'm filtering on another property (for example on the combination of first and lastname), it works!
Currently, what happens is that for each Contact that already exist in the GAL, a creation instead of an update is done (because the FindItems method returns nothing), and as a result, the same Contact is created X time (instead of beeing created once, and then updated X-1 time).
The question is of course, how can I know if a Contact exists in an exchange folder when it already exists in the GAL?
Current code:
ItemView view = new ItemView(5)
{
PropertySet = new PropertySet(BasePropertySet.FirstClassProperties)
};
SearchFilter f = new SearchFilter.IsEqualTo(ContactSchema.EmailAddress1, email);
FindItemsResults<Item> contactItems = _service.FindItems(folderId, f, view);
int resultCount = contactItems.Count(); // Always 0 if GAL, otherwise it works
Finally, I solved my problem with an extended property by using the SetExtendedProperty method. In this extended field, I just put an Id and that solved the problem.
But that does not explain why the search is not working with an email address... If someone knows the answer, I'm still interested :)
The new search looks like this:
ItemView view = new ItemView(nb);
view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, _extendedPropDef);
SearchFilter f = new SearchFilter.IsEqualTo(_extendedPropDef, contact.Id);
FindItemsResults<Item> contactItems = _service.FindItems(folderId, f, view);
With this code, everything works as expected...

c# EWS 2007 Address from empty

I'm working on a WinForms Application that uses EWS to read mails of our Exchange Server. The Exchange is at Version 2007. I could successfully read, move, delete and send emails through EWS. I'm using Autodiscover to authenticate and select the Mailbox. The only problem is that I never get any sender e-mail address. The only thing I get is the name of the sender but no address.
This is my code so far:
Service1 = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
Service1.Credentials = new WebCredentials(Properties.Settings.Default.Username, Properties.Settings.Default.Password);
Service1.Url = new Uri(Properties.Settings.Default.Serviceurl);
EmailMessage messageAtt = EmailMessage.Bind(Service1, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments, ItemSchema.HasAttachments, EmailMessageSchema.IsRead));
EmailMessage messageData = (EmailMessage)item;
foreach (Attachment attachment in messageAtt.Attachments)
{
String from = messageData.Sender.Address.ToString();
}
This is what I get when I debug:
Can anyone give me a suggestion what I am mistaking here? Is there a Problem with what I wrote or could it even be a set up problem of the exchange Server?
The problem seems to be the definition of the EmailMessage object:
By defining the EmailMessage with explicit conversion not all attributes are geting transfered to the new object. If you try it with the upper EmailMessage object which gets defined by the .Bind() method, it wont work either. The reason that happens is due to the PropertySet passed as 3rd parameter. The only solution I found is to create a 3rd object:
EmailMessage messageInfo = EmailMessage.Bind(useService, item.Id);
The disadvantage of this Object is, that you won't be able to see if the item has an attachement or not.
Hope this helps anyone not wasing his time on a stupid mistake like that ;)

Categories

Resources