При поиске глобального списка адресов есть способ сделать частичный поиск, а не просто "startWith",

У меня есть следующий код для поиска глобальной адресной книги по определенной строке:

"CONF"

var esb = new ExchangeServiceBinding();
esb.Url = @"https://myurl.com/EWS/Exchange.asmx";

esb.Credentials = new NetworkCredential(_user,_pwd, _domain);

var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "CONF"};

ResolveNamesResponseType response = esb.ResolveNames(rnType);
ArrayOfResponseMessagesType responses = resolveNamesResponse.ResponseMessages;
var responseMessage = responses.Items[0] as ResolveNamesResponseMessageType;

ResolutionType[] resolutions = responseMessage.ResolutionSet.Resolution;

проблема в том, что он, похоже, выполняет поиск "начинается с", поэтому у меня есть имя:

"CONF-123" будет отображаться, но если у меня есть имя "JOE-CONF", это не будет.

Как выполнить частичный поиск строк в этой строке

var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "CONF-"};

Я надеялся, что есть что-то вроде:

var rnType = new ResolveNamesType {ReturnFullContactData = true, UnresolvedEntry = "%CONF-%"};

но это не работает.

Ответы

Ответ 1

EDIT: Ян 4,2016 - Добавлен образец кода для поиска AD.

Что не будет работать

Поиск GAL через ResolveNames всегда использует соответствие префиксной строки для неоднозначного разрешения имен (ARN). Хотя в документации EWS это явно не указано, документация Exchange ActiveSync. EWS и Exchange ActiveSync - это просто протоколы; они оба полагаются на ARN внизу, так что вы застряли в сочетании с префиксом, используете ли вы протокол ActiveSync или EWS.

Вот соответствующая цитата из документации Exchange ActiveSync (https://msdn.microsoft.com/en-us/library/ee159743%28v=exchg.80%29.aspx)

Строка текстового запроса, которая предоставляется команде поиска, используется в совпадении с префиксной строкой

.

Что будет работать

Лучшее, что нужно сделать, зависит от вашего варианта использования, но вот несколько идей:

Поиск Active Directory в вашей клиентской программе (программа, содержащая код, который вы указали в своем вопросе)

Настройте свой собственный сервис для поиска в GAL. Ваша клиентская программа будет подключаться как к Exchange, так и к вашей службе. Или ваш сервис может проксировать EWS, так что клиентская программа должна подключаться только к вашей службе.

Как вы можете получить данные GAL? Одним из способов было бы использовать EWS ResolveNames несколько раз, чтобы получать данные GAL, 100 записей за раз и кэшировать эти данные в вашей службе. Сначала загрузите все "a", затем все "b" и т.д. Конечно, в GAL может быть больше 100 "а", поэтому просто получение всех "а" может выполнять несколько запросов - вы построили бы следующую строку поиска на основе последней записи, возвращаемой из каждого поиска. Это может быть медленным и болезненным. Возможно, вам захочется кэшировать эти данные в базе данных и периодически обновлять их.

Вы также можете попасть в GAL через MAPI. Вы можете использовать MAPI напрямую (https://msdn.microsoft.com/en-us/library/cc765775%28v=office.12%29.aspx) или через вспомогательную библиотеку, такую ​​как Redemption (http://www.dimastr.com/redemption/home.htm). Независимо от того, используете ли вы MAPI напрямую или через Redemption, вам необходимо установить Outlook (или Exchange) на компьютер, на котором работает ваш код. Из-за этого ограничения может быть лучше не использовать MAPI в вашей клиентской программе, а вставлять его в службу, запущенную на каком-либо сервере, и подключить вашу клиентскую программу к этой службе.

Пример кода AD

Другой ответ предоставил пример кода для поиска в Active Directory. Я добавляю образец кода, который может быть лучше подходит для общего использования людьми, которые могут найти этот вопрос через поиск. По сравнению с другим образцом, приведенный ниже код имеет следующие улучшения:

  • Если строка поиска содержит любые специальные символы (например, скобки), они экранируются, поэтому строковая строка фильтра действительна.

  • Поиск по простому учетному имени многого не достаточно. Если "Дэвид Смит" имеет имя учетной записи "dsmith", поиск "Давида" по имени samaccount не найдет его. В моем примере показано, как выполнять поиск по большему количеству полей и дает некоторые поля, которые можно искать.

  • Начиная с корня типа "GC:" является более надежным, чем попытка создать запись LDAP из Domain.GetComputerDomain().

  • Все IDisposable должны быть удалены (обычно, используя их в конструкции using).

    // Search Active Directory users.
    public static IEnumerable<DirectoryEntry> SearchADUsers(string search) {
        // Escape special characters in the search string.
        string escapedSearch = search.Replace("*", "\\2a").Replace("(", "\\28")
            .Replace(")", "\\29").Replace("/", "\\2f").Replace("\\", "\\5c");
    
        // Find entries where search string appears in ANY of the following fields
        // (you can add or remove fields to suit your needs).
        // The '|' characters near the start of the expression means "any".
        string searchPropertiesExpression = string.Format(
            "(|(sn=*{0}*)(givenName=*{0}*)(cn=*{0}*)(dn=*{0}*)(samaccountname=*{0}*))",
            escapedSearch);
    
        // Only want users
        string filter = "(&(objectCategory=Person)(" + searchPropertiesExpression + "))"; 
    
        using (DirectoryEntry gc = new DirectoryEntry("GC:")) {
            foreach (DirectoryEntry root in gc.Children) {
                try {
                    using (DirectorySearcher s = new DirectorySearcher(root, filter)) {
                        s.ReferralChasing = ReferralChasingOption.All;
                        SearchResultCollection results = s.FindAll();
                        foreach (SearchResult result in results) {
                            using (DirectoryEntry de = result.GetDirectoryEntry()) {
                                yield return de;
                            }
                        }
                    }
                } finally {
                    root.Dispose();
                }
            }
        }
    }
    

Ответ 2

Несмотря на то, что поиск по шаблону в EWS невозможен, это возможно в поиске AD. Запросы AD поддерживают подстановочные знаки. т.е., * CONF * можно искать в AD, который будет возвращать все результаты, которые содержат "CONF". На основании результатов запросите EWS для соответствующего объекта Exchange. Вам нужно найти параметр, с помощью которого вы можете найти соответствующую запись EWS. Я думаю, адрес электронной почты (имя пользователя) должен быть достаточным, чтобы найти соответствующий объект обмена.

AD Искать фрагмент кода...

private SearchResultCollection SearchByName(string username, string password, string searchKeyword)
{
    DirectorySearcher ds = new DirectorySearcher(new DirectoryEntry("LDAP://" + Domain.GetComputerDomain().ToString().ToLower(), username, password));
    ds.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=*" + searchKeyword + "*))";
    ds.SearchScope = SearchScope.Subtree;
    ds.ServerTimeLimit = TimeSpan.FromSeconds(90);
    return ds.FindAll();
}

Пример запроса AD здесь.

Ответ 3

Неопределенный поиск по индексированному текстовому полю может быть выполнен только с помощью префикса (или суффикса...). Поэтому Exchange, вероятно, реализует запрос как LIKE 'CONF%'.

Я просмотрел документацию, и вы не можете ее обойти, - полное сканирование таблицы (что должно быть в случае% CONF%), по-видимому, не имеет смысла.

Ответ 4

Я также пытался искать GAL из PHP, используя php-ews. На веб-странице пользователь начинает вводить имя для поиска, как только введено 2 символа, вызов read_contacts.php производится с использованием введенных 2 символов. read_contacts.php использует запрос EWS ResolveNames для получения ограниченного списка контактов. Затем код фильтрует по некоторым критериям, а также проверяет, что имя displayName начинается с введенных символов. Затем он возвращает отфильтрованный список:

<?php
$staffName = $_GET['q'];    
require_once './php-ews/EWSType.php';
require_once './php-ews/ExchangeWebServices.php';
require_once 'php-ews/EWSType/RestrictionType.php';
require_once 'php-ews/EWSType/ContainsExpressionType.php';
require_once 'php-ews/EWSType/PathToUnindexedFieldType.php';
require_once 'php-ews/EWSType/ConstantValueType.php';
require_once 'php-ews/EWSType/ContainmentModeType.php';
require_once 'php-ews/EWSType/ResolveNamesType.php';
require_once 'php-ews/EWSType/ResolveNamesSearchScopeType.php';
require_once 'php-ews/NTLMSoapClient.php';
require_once 'php-ews/NTLMSoapClient/Exchange.php';

$host = '[exchange server]';
$user = '[exchange user]'; 
$password = '[exchange password]';

$ews = new ExchangeWebServices($host,  $user, $password);

$request = new EWSType_ResolveNamesType();

$request->ReturnFullContactData = true;
$request->UnresolvedEntry = $staffName;


$displayName = '';
$i = 0;
$staff_members = false;
$response = $ews->ResolveNames($request);
if ($response->ResponseMessages->ResolveNamesResponseMessage->ResponseClass == 'Error' && $response->ResponseMessages->ResolveNamesResponseMessage->MessageText == 'No results were found.') {
}
else {
    $numNamesFound = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->TotalItemsInView;
    $i2=0;
    for ($i=0;$i<$numNamesFound;$i++) {
        if ($numNamesFound == 1) {
            $displayName = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution->Contact->DisplayName;
        }
        else {
            $displayName = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution[$i]->Contact->DisplayName;
        }
        echo "DisplayName: " . $displayName . "\n";     
        if (stripos($displayName, 'External') == true) {
        }
        else {
            $searchLen = strlen($staffName);
            if (strcasecmp(substr($displayName, 0, $searchLen), $staffName) == 0) {
                if ($numNamesFound == 1) {
                    $emailAddress = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution->Mailbox->EmailAddress;
                }
                else {
                    $emailAddress = $response->ResponseMessages->ResolveNamesResponseMessage->ResolutionSet->Resolution[$i]->Mailbox->EmailAddress;
                }
                $staff_members[$i2] = array( 'name' => $displayName,'email' => $emailAddress );
                $i2++;
            }
        }
    }
    $staffJson = json_encode($staff_members);
echo $staffJson;
}

В большинстве случаев это, похоже, работает, т.е. "mi", "jo" возвращает Майка, Майкла или Джо, Джона, за исключением случаев, когда я посылал "Si" или "si", для Simon, затем ResolveNames возвращает первые 100 записей из GAL.

В настоящее время я увеличил минимальное количество символов для ввода до 3, то есть: "sim", и это работает. Проблема будет заключаться в том, когда мы получим штат с именами всего по 2 символа.

Я просто подумал, что поделился бы кодом, чтобы узнать, помогает ли он, и узнать, знает ли кто-нибудь, почему мой si не работает должным образом.

Я обращаюсь к Exchange 2010