How to reply to an email using the EWS Managed API? - c#

I have created an application that uses the EWS Managed API 2.2.
This application uses pull notifications to get new emails and saves a copy of the email in a database.
Then in the application I want to get the email from database and reply to it.
In order to reply to the message, I need to retrieve it from EWS using the ItemId I have stored in my database.
Of course I can create a new EmailMessage and send it but then the new email will have a different ConversationId which is not acceptable for the application scenario.
So, in order to achieve this I use the the following line of code
EmailMessage.Bind(service, itemId);
For this method to work I have to instantiate the ItemId from my database but the ItemId constructor takes as parameter only the UniqueId, and creates it with null ChangeKey.
If I use this ItemId (with null ChangeKey) I get the following error:
Microsoft.Exchange.WebServices.Data.ServiceResponseException: The specified object was not found in the store.
I think that this is because of the null ChangeKey. Am I correct?
Is there a workaround about this?

Instead of identifying a message by ItemId, use EntryID. Using EntryID, you can bind to the same email without needing ChangeKey.
Here is the definition of such property:
ExtendedPropertyDefinition EntryIDProperty = new ExtendedPropertyDefinition(0x0FFF, MapiPropertyType.Binary);
When you do your search for messages, make sure you instruct EWS to include such property in the list of retrieved items.
Here is an example to obtain EntryIDs when you invoke FindItems:
ExtendedPropertyDefinition EntryIDProperty = new ExtendedPropertyDefinition(0x0FFF, MapiPropertyType.Binary);
ItemView item_view = new ItemView(10) { PropertySet = new PropertySet(ItemSchema.Id, EntryIDProperty) };
var result = service.FindItems(WellKnownFolderName.Inbox, item_view);
foreach (var item in result.Items)
{
byte[] entry_id = (byte[])item.ExtendedProperties.Single(x => x.PropertyDefinition == EntryIDProperty).Value;
string entry_id_hex = ByteArrayToHexString(entry_id); //This is the entry ID that you should store
}
Use the following method to convert a EntryID to ItemID if you want to use EmailMessage.Bind:
This method accepts string EntryID.
mailbox_address is the SMTP address of the mailbox (e.g. test#domain.com)
'service' is the ExchangeService object.
private ItemId ConvertEntryIdToItemId(string entryid, string mailbox_address, ExchangeService service)
{
AlternateId id = new AlternateId(IdFormat.HexEntryId, entryid, mailbox_address);
AlternateId new_id = (AlternateId)service.ConvertId(id, IdFormat.EwsId);
ItemId item_id = new_id.UniqueId;
return item_id;
}
Now you can use the returned ItemId to bind your EmailMessages.

The specified object was not found in the store.
That error generally means that you don't have rights to the Mailbox your trying to access or the Item your trying to access no longer exists in the store. Eg in a pull notification application that can mean the Item that you being notified about has already been deleted or moved to another folder (in each of these case the Item will be assigned a new Id). If you also listing to Move event you should be able to see the corresponding move event which will have the OldItemId that correlates the newMailEvent notification.
The Change Key only matters when your updating an Item so in the case the error you getting on Bind means that Item your trying doesn't exist (or has been moved) or you don't have rights to access it binding with just the UniqueId is perfectly okay see also https://msdn.microsoft.com/en-us/library/office/dn605828(v=exchg.150).aspx
Cheers
Glen

Related

Dynamics CRM. Delete value in field with type 'File'

I have a custom attribute with the type 'file' and I need to delete the file via a plugin.
I checked the documentation located here: https://learn.microsoft.com/en-us/powerapps/developer/data-platform/file-attributes and it says that for .net I have to use DeleteFileRequest.
I checked the documentation for this request and looks like it requires file ID, but I'm not sure where I can get it.
I tried to pass parameters in the ParameterCollection, similar to the Download Request, but it doesn't work this way:
var req = new DeleteFileRequest()
{
Parameters = new ParameterCollection()
{
new KeyValuePair<string, object>("Target", new EntityReference("xxx_entityName", locId)),
new KeyValuePair<string, object>("FileAttributeName", "xxx_attributeName")
}
};
Didn't find any samples for this request in Google, so if someone has any experience with this - please share.
Thank you.
You can get simply get File GUID from simple Retrieve or RetrieveMultiple message request in the CRM Organization Service object and after retrieving it, you can simply make the following deleteFileRequest:
DeleteFileRequest deleteFileRequest = new DeleteFileRequest()
{
FileId = new Guid(entityResult.Entities[0].Attributes["FieldLogicalName"].ToString())
};
DeleteFileResponse deleteFileResponse = (DeleteFileResponse)organizationService.Execute(deleteFileRequest);
This Thread is quite old, but the answers were not that exact:
First of all, if you look at the entity containing the file field with oData, you will find that the file field contains a Guid (and there is a second _name field with the file name). However at least spkl wont generate an earlybound for the file field.
So the first discovery is: You need to access the field LateBound and the containing Data Type is Guid! This code will work : myEntity.GetAttributeValue<Guid>("<filefieldname>");
And now Waleeds code will work, I've wrapped it in an Extension function to not clutter my logic:
public static void DeleteFile(this Entity entity, string fieldName, IOrganizationService service)
{
if (!entity.Contains(fieldName))
return;
var fileId = entity.GetAttributeValue<Guid>(fieldName);
if (fileId == Guid.Empty)
return;
DeleteFileRequest deleteFileRequest = new DeleteFileRequest()
{
FileId = fileId
};
service.Execute(deleteFileRequest);
}
And finally call it after my Code has finished parsing the file:
var fileIdEntity = Service.Retrieve(target.LogicalName, target.Id, new Microsoft.Xrm.Sdk.Query.ColumnSet(fieldname)).GetAttributeValue<Guid>(fieldname);
//Downloading and Parsing here
fileIdEntity.DeleteFile(fieldname, Service);
By the way: Upload and Delete both trigger Plugins registered on the file field as the Guid in the file field changes.
I was able to get the File ID (GUID) that is required for the DeleteFileRequest. It presents in the target Entity in file attribute during file upload. So in the plug-in registered at ModifiedOn (can't use File field as a trigger), you can see in the file field the ID. You can save it somewhere for later use.
Unfortunately still not sure how to get it for the already uploaded file. Please share your thoughts if you know how to do it.

TFS 2018 API: Can access workitems but not workitem API end point

I created a middleware app that will pull work item data from TFS.
I was able to do this using the workitems end point.
http://sampleserver:8080/tfs/sampleproject/_apis/wit/wiql?api-version=4.0/workitems?ids=1,2,3
Now, I also need to get the work item links per work item. Per docu I would need to access the workitem with expand items. But unfortunately, work item end point does not seem to work.
http://sampleserver:8080/tfs/sampleproject/_apis/wit/wiql?api-version=4.0/workitem/3
Am I missing something here?
According to your description, looks like you just want the URL of created WorkItem, so that anyone when click on URL, created Work Item will be Open.
https://tfsurl:8080/tfs/DefaultCollection/PatrickProject/_workitems/edit/172/
The URL should be above format and here DefaultCollection is the collection name and the PatrickProject is the project name. I used this url and got rid of the id '172' in this case and use the ID of newly created work item. This would return the URL to go to the work item HTML page.
So it's a fixed format, if you have Newly Created WorkItem ID and collection name , project name, you just need to follow above format and change the last value of work item ID. That's it , ignore of which work item type you created.
If you want do this with code, do not use Rest API, you need to use client API, sample snippet:
var tfsURI = new Uri("http://test:8080/tfs");
var networkCredential1 = new NetworkCredential("test", "test!");
ICredentials credential = (ICredentials)networkCredential1;
Microsoft.VisualStudio.Services.Common.WindowsCredential winCred = new Microsoft.VisualStudio.Services.Common.WindowsCredential(credential);
VssCredentials vssCredentials = new VssCredentials(winCred);
using (TfsTeamProjectCollection collection = new TfsTeamProjectCollection(tfsURI, vssCredentials))
{
collection.EnsureAuthenticated();
TswaClientHyperlinkService hyperlinkService =
collection.GetService<TswaClientHyperlinkService>();
String TFSurl = hyperlinkService.GetWorkItemEditorUrl(17648).ToString(); //17648 WorkItem ID
}
Hope this Helps!
To get specific work item information you need to use the Get Workitem API call so try http://sampleserver:8080/tfs/sampleproject/_apis/wit/workitems/3?api-version=4.0 instead.
You can also use this http://sampleserver:8080/tfs/sampleproject/_apis/wit/workitems/3?$expand=Links&api-version=4.0 and this will return the work with Id of 3 and all it's links (parent, attached files, changetset, etc.)
Notice that the api-version=4.0 with change depending on the version of TFS/Service you are using and should always be the last string in the REST call.

MailKit From Address

Im using mailKit in asp mvc core to collect email from a IMAP mailbox.
I return the message using the command
var message = inbox.GetMessage(uid)
This returns all the results of the message. From here i want to access the sender email address (not including the name). After breakpointing on the above line i can see that the variable message has the following property
message
-From
--From(Array)
---From(item)
----Name (name of the sender)
----Address(email of the sender)
When referencing the above above using the message i am able to receive the name, however the address is not listed (within intelisence, nor will it build)
var name = message.From[0].Name.ToString()
Does anyone know why this would be visible as properties of the variable but not accessible via the code?
i simply want to
var name = message.From[0].Name.ToString()
The MimeMessage.From property is a InternetAddressList (more-or-less List<InternetAddress>).
InternetAddress is an abstract base class for MailboxAddress and GroupAddress which only contains a Name property as you've discovered.
In order to get the Address, you first need to cast it to a MailboxAddress... but only if it is actually a MailboxAddress or you'll get a cast exception.
InternetAddressList has a convenience property called Mailboxes which can be used to iterate over a flattened list of the MailboxAddresses contained within.
You can use this code block.
message.From.OfType<MailboxAddress>().Single().Address;
Another solution:
message.From.Mailboxes.Single().Address;

Using Microsoft's EWS to create online Lync/Skype meeting

Anybody knows how to create meeting request with online conference(Lync/Skype) using EWS?
So my approach is first getting an online and regular meeting created via Outlook and then simulate the creation of event with the same property.
Here is my code snippet for getting the meeting (calendarView is already initialized with start date, end date etc.):
ExtendedPropertyDefinition extendedOnlineMeetingProperty =
new ExtendedPropertyDefinition(new Guid("{00062008-0000-0000-c000-000000000046}"), 34112,
MapiPropertyType.Integer);
var properties = new PropertySet(
ItemSchema.Id,
AppointmentSchema.ICalUid,
ItemSchema.Subject,
AppointmentSchema.Start,
AppointmentSchema.End,
AppointmentSchema.Organizer,
AppointmentSchema.Location,
AppointmentSchema.LegacyFreeBusyStatus,
AppointmentSchema.IsCancelled,
AppointmentSchema.ICalRecurrenceId,
AppointmentSchema.MyResponseType, // Mandatory Meeting.MyResponseType can be retrieved without a search in the participant list
ItemSchema.LastModifiedTime,
AppointmentSchema.IsOnlineMeeting,
AppointmentSchema.IsMeeting,
ItemSchema.DisplayTo) { };
properties.Add(extendedOnlineMeetingProperty);
var activeResults = service.FindAppointments(WellKnownFolderName.Calendar, calendarView).ToList();
if (activeResults.Count > 0)
{
service.LoadPropertiesForItems(activeResults, properties);
}
I got the property IsOnlineMeeting with the correct bool value (tested -
created online and regular meeting with Outlook) in variable activeResults but I do not understand where to get conference link and other Lync/Skype properties needed for joining a conference.
Also I am not sure where and how to assign the values of Lync/Skype conference URL and other properties.
Sometimes I ask myself if it's worth it to developed some app based on MS products since their documentation suck.
After one week of cursing MS I have found a solution. Using the tool MFCMAPI you can check what property and their values your item in mailbox have.
download the program link
build and run it
Session - Logon - choose your mail profile - pick the mailbox and double click
actions - open special folder - calendar - double click on calendar
open the item with online S4B/Lync conference
the UC* properties are the one I was looking for.
If you open the property you can see something like this on the top:
ag: 0x8096001E
Type: PT_STRING8
DASL: http://schemas.microsoft.com/mapi/string/{00020329-0000-0000-C000-000000000046}/UCMeetingSetting
Named Prop Name: UCMeetingSetting
Named Prop Guid: {00020329-0000-0000-C000-000000000046} = PS_PUBLIC_STRINGS
So my definition of the extended property was wrong. It is not only one property but actually you need all 7 of them.
So the definition should be:
private static ExtendedPropertyDefinition CreateOnlineMeetingProperty()
{
ExtendedPropertyDefinition extendedUCMeetingSetting =
new ExtendedPropertyDefinition(new Guid("{00020329-0000-0000-C000-000000000046}"), "UCMeetingSetting",
MapiPropertyType.String);
return extendedUCMeetingSetting;
}
With the correct extended definition you can get the values from the item easily.
accessing the Value of ExtendedProperties
Calling TryGetProperty
var activeResults = service.FindAppointments(new
FolderId(WellKnownFolderName.Calendar, resource.Email),calendarView).ToList();
service.LoadPropertiesForItems(activeResults, properties);
foreach (Appointment result in activeResults)
{
// 1.
var b = result.ExtendedProperties[1].Value;
// 2.
string UCMeetingSetting;
result.TryGetProperty(extendedUCMeetingSetting, out UCMeetingSetting);
}
using steps above you can get whatever extended property you want, not only Unified Communications (UC) properties.
Ok, I managed to have this working (almost!) by setting just one of the extended properties:
appointment.SetExtendedProperty(
new ExtendedPropertyDefinition(
new Guid("00020329-0000-0000-C000-000000000046"),
"OnlineMeetingExternalLink",
MapiPropertyType.String
),
skypeMeeting.JoinUrl
);
I say almost because the appointment doesn't look exactly like a Skype meeting when you open it in Outlook: doesn't have the footer will the Join link and phone number etc.
Maybe there are other differences, but for now we see it in Skype for business with the Join button and also we see the it in the Outlook reminder with the Join button.
As a workaround, we have to hard-code the body content of the appointment.
Also conference ID, can be taken using UCWA 2.0 (https://learn.microsoft.com/en-us/skype-sdk/ucwa/myonlinemeetings_ref)
We used UCWA 2.0 to create the Skype conference call and attached it to the EWS appointment object.

Access Public String MAPI Properties through EWS

I've created custom properties in an Appointment in Outlook through JavaScript in an App for Outlook, which have saved successfully. I have checked the MAPI properties of this appointment and I can see the property as a JSON dictionary.
MFCMAPI display of the stored property:
I'm now trying to retrieve this through EWS in a C# application. For troubleshooting purposes, I also attempt to retrieve another property, with the ID of 33336.
ExtendedPropertyDefinition epd = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "cecp-propertyNames", MapiPropertyType.String);
ExtendedPropertyDefinition epd2 = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.Appointment, 33336, MapiPropertyType.String);
//Create our sync window. This is the period of appointments it will capture and sync
CalendarView cv = new CalendarView(START_DATE_SYNC, END_DATE_SYNC);
cv.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, epd, epd2);
FolderId calendarFolderId = new FolderId(WellKnownFolderName.Calendar, room.Address);
FindItemsResults<Appointment> fappts = service.FindAppointments(calendarFolderId, cv);
Exchange returns the appropriate appointment items, but when accessing the extended properties, only the property with the ID of 33336 (the test one) is returned. Any time I try and retrieve a PS_PUBLIC_STRINGS item, nothing comes back.
Your imgur link is frustratingly cropped. I cannot see the named property guid column to check if it matches what you're trying there in the EWS code. So I can only guess that I think you've got the wrong property set.
Have you tried looking at the item with EWS Editor to see if it finds the property you're looking for naturally? If it does, you can read off the details you need to construct your EWS code.

Categories