Resolve ambiguous names by using EWS in Exchange 2013

Learn how to use the EWS Managed API or EWS to resolve ambiguous names by getting possible matches from Active Directory Domain Services (AD DS) or a contacts folder in your user's mailbox.

A user in your organization is given a hand-written list of names and addresses for employees that attended a training session. They want to send an email with some additional information to people on the list, but they can't read everyone's email address. If you want to solve this problem for your users in your application, EWS can help. You can use the ExchangeService.ResolveName EWS Managed API method or the ResolveNames EWS operation to return a list of potential matches for a selection of text, such as part of a last name. The returned items can be public user mailboxes, distribution groups, and contacts.

Note that Exchange saves email addresses with prefixed routing types, such as smtp or sip, in a multivalue array. The ResolveName method and the ResolveNames operation perform a partial match against each value of that array when you add the routing type at the beginning of the unresolved name, such as "sip:User1". If you don't specify a routing type, the method or operation will default to smtp, match it to a primary smtp address property, and not search the multivalue array. For example, if you search for User1 and do not include the sip prefix, you will not receive sip:User1@Contoso.com as a result, even if that is a valid mailbox.

You can only specify one ambiguous name in a single request. If you have a list of ambiguous names to resolve, you will need to loop through the list and call the method or operation for each entry. Candidates from a user's Contacts folder will have a non-null item ID value, which can then be used in a Contact.Bind method call or GetItem operation request to retrieve additional information. If the candidate is a distribution group, you can use the ExpandGroup(ItemId) EWS Managed API method or the ExpandDL EWS operation to get the list of members. If the returnContactDetails parameter or the ReturnFullContactData EWS attribute is set to true, Active Directory entries returned via a ResolveName method or ResolveNames operation will include additional properties that describe the contact. The returnContactDetails parameter or the ReturnFullContactData attribute does not affect the data that is returned for contacts and contact groups.

Resolve ambiguous names by using EWS Managed API

You can use the ResolveName method to find candidates that match the ambiguous name you pass. You can use overloads of the ResolveName method to search for candidates in five different ways.

Table 1. Overloaded ResolveName methods

Method How it works
ResolveName(String) Finds contacts in the user's Contacts folder and the Global Address List (GAL) — in that order. The string variable is the ambiguous name you are trying to resolve.
ResolveName(String, ResolveNameSearchLocation, Boolean) Finds contacts in the default Contacts folder and/or the Global Address List (GAL). The string value is the ambiguous name, the search location specifies the Contacts folder and/or the GAL, and the Boolean value indicates whether to return the full contact information.
ResolveName(String, ResolveNameSearchLocation, Boolean, PropertySet) Finds contacts in the default Contacts folder and/or Global Address List (GAL). This method enables you to set the properties that are returned.
ResolveName(String, IEnumerable<FolderId>, ResolveNameSearchLocation, Boolean) Finds contacts in specified contact folders and/or the Global Address List (GAL). You can use this method to pass a collection of folders to search. This enables you to look in contact folders other than the default Contacts folder.
ResolveName(String, IEnumerable<FolderId>, ResolveNameSearchLocation, Boolean, PropertySet) Finds contacts in the Global Address List (GAL) and/or in specific contact folders. This method enables you to set the properties that are returned.

Let's start with a simple example. The following example shows how to resolve the text string "dan" and output the name and email address of each candidate found. This example assumes that service is a valid ExchangeService object and that the user has been authenticated to an Exchange server.

// Resolve the ambiguous name "dan".
   NameResolutionCollection resolvedNames = service.ResolveName("dan");
   // Output the list of candidates.
   foreach (NameResolution nameRes in resolvedNames)
   {
      Console.WriteLine("Contact name: " + nameRes.Mailbox.Name);
      Console.WriteLine("Contact e-mail address: " + nameRes.Mailbox.Address);
      Console.WriteLine("Mailbox type: " + nameRes.Mailbox.MailboxType);
   }

The response returns a maximum of 100 candidates, although there might be more than 100 potential candidates. To determine whether only the first 100 candidates of a larger number of candidates were returned, check the value of IncludesAllResolutions in the NameResolutionCollection object. If the value is true, there are no more possible candidates; if the value is false, the method only returned the first 100 of a larger number of potential candidates.

If you work in a large organization, it's likely that a name like "dan" will return the maximum number of 100 candidates. To reduce the number of candidates returned, limit where you search. The next example uses the ResolveNameSearchLocation enumeration to specify where to search to resolve the ambiguous name.

// Resolve the ambiguous name "dan".
// Only use the Contacts folder.
   NameResolutionCollection resolvedNames = service.ResolveName("dan", ResolveNameSearchLocation.ContactsOnly, false);
   // Output the list of candidates.   
   foreach (NameResolution nameRes in resolvedNames)
   {
      Console.WriteLine("Contact name: " + nameRes.Mailbox.Name);
      Console.WriteLine("Contact e-mail address: " + nameRes.Mailbox.Address);
      Console.WriteLine("Mailbox type: " + nameRes.Mailbox.MailboxType);
   }

If you store your contacts in a folder other than the well-known Contacts folder, use one of the overloaded methods to specify where to look for candidates. The following example creates a folder list for the ResolveName method based on the folder ID. The FolderId has been shortened for readability.

// Create a list to store folders to search.
List<FolderId> folders = new List<FolderId>();
// Add a folder to the list based on the FolderId.
folders.Add(new FolderId("AABR8mboAAA="));
// Resolve the ambiguous name "dan".
// Only use the folders specified.
NameResolutionCollection resolvedNames = service.ResolveName("dan", folders, ResolveNameSearchLocation.ContactsOnly, false);
   foreach (NameResolution nameRes in resolvedNames)
   {
      Console.WriteLine("Contact name: " + nameRes.Mailbox.Name);
      Console.WriteLine("Contact e-mail address: " + nameRes.Mailbox.Address);
      Console.WriteLine("Mailbox type: " + nameRes.Mailbox.MailboxType);
   }

If you apply filters and no candidates are returned, the NameResolutionCollection will contain zero entries. You can verify this by looking at the Count property of the collection.

Resolve ambiguous names by using EWS

You can use the ResolveNames EWS operation to identify possible candidates for an ambiguous name. The UnresolvedEntry element contains the ambiguous name you want to resolve. The following example shows how to resolve the name Sadie. This is also the XML request that the EWS Managed API uses when you use the ResolveName method, except that it uses a different name for valid output examples.

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/"
               xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
  <soap:Body>
    <ResolveNames xmlns="https://schemas.microsoft.com/exchange/services/2006/messages"
                  xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types"
                  ReturnFullContactData="true">
      <UnresolvedEntry>Sadie</UnresolvedEntry>
    </ResolveNames>
  </soap:Body>
</soap:Envelope>

The response returns a maximum of 100 candidates, although there might be more than 100 potential candidates To determine whether only the first 100 candidates of a larger number of candidates were returned, check the value of the IncludesLastItemInRange attribute of the ResolutionSet element. If the value is true, there are no more possible candidates; if the value is false, the operation only returned the first 100 of a larger number of potential candidates.

The following example shows the XML response when one candidate is found. Remember, the ResolutionSet can contain up to 100 candidates, each one represented by the Resolution element and its child elements.

<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <ResolveNamesResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" 
                          xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types" 
                          xmlns="https://schemas.microsoft.com/exchange/services/2006/messages">
      <m:ResponseMessages>
        <m:ResolveNamesResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:ResolutionSet TotalItemsInView="1" IncludesLastItemInRange="true">
            <t:Resolution>
              <t:Mailbox>
                <t:Name>Sadie Daniels</t:Name>
                <t:EmailAddress>Sadie@Contoso.com</t:EmailAddress>
                <t:RoutingType>SMTP</t:RoutingType>
                <t:MailboxType>Mailbox</t:MailboxType>
              </t:Mailbox>
              <t:Contact>
                <t:DisplayName>Sadie Daniels</t:DisplayName>
                <t:EmailAddresses>
                  <t:Entry Key="EmailAddress1">SMTP:Sadie@Contoso.com</t:Entry>
                </t:EmailAddresses>
                <t:ContactSource>ActiveDirectory</t:ContactSource>
              </t:Contact>
            </t:Resolution>
          </m:ResolutionSet>
        </m:ResolveNamesResponseMessage>
      </m:ResponseMessages>
    </ResolveNamesResponse>
  </soap:Body>
</soap:Envelope>

You're not always going to come up with candidates for your ambiguous name. The following example shows the XML response, as an error, when no candidates are found.

<?xml version="1.0" encoding="utf-8" ?>
<soap:Envelope xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <soap:Body>
    <ResolveNamesResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" 
                          xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types" 
                          xmlns="https://schemas.microsoft.com/exchange/services/2006/messages">
      <m:ResponseMessages>
        <m:ResolveNamesResponseMessage ResponseClass="Error">
          <m:MessageText>No results were found.</m:MessageText>
          <m:ResponseCode>ErrorNameResolutionNoResults</m:ResponseCode>
          <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
        </m:ResolveNamesResponseMessage>
      </m:ResponseMessages>
    </ResolveNamesResponse>
  </soap:Body>
</soap:Envelope>

See also