搜索全局地址列表时,有没有办法进行部分搜索,而不仅仅是" startsWith"

时间:2015-10-16 16:37:08

标签: c# outlook vsto exchange-server exchangewebservices

我有以下代码通过某个字符串搜索全局通讯簿:

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

但这似乎不起作用。

4 个答案:

答案 0 :(得分:5)

编辑: 2016年1月4日 - 添加了搜索AD的示例代码。

什么不行?

通过ResolveNames搜索GAL始终使用前缀字符串匹配来模糊名称解析(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

的相关引用
  

使用提供给Search命令的文本查询字符串   在前缀字符串匹配

什么可行?

最好的办法取决于你的用例,但这里有一些想法:

在客户端程序中搜索Active Directory(包含您在问题中显示的代码的程序)

设置您自己的服务以搜索GAL。您的客户端程序将连接到Exchange和您的服务。或者您的服务可以代理EWS,因此客户端程序只需要连接到您的服务。

您将如何获得GAL数据?一种方法是重复使用EWS ResolveNames,一次获取GAL数据,100个条目,并在服务中缓存此数据。首先,检索所有" a" s,然后检索所有" b"等等。当然,可以有超过100个" a" s GAL,所以只需要获得所有" a" s就可以进行多次搜索 - 您将根据每次搜索返回的最后一个条目构建下一个搜索字符串。这可能是缓慢而痛苦的。您可能希望将此数据缓存在数据库中并定期刷新。

您也可以通过MAPI访问GAL。您可以直接使用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,而是将其粘贴在某个服务器上运行的服务中,并让客户端程序连接到该服务。

广告代码示例

另一个答案提供了搜索Active Directory的示例代码。我正在添加一个代码示例,可能更适合通过搜索找到此问题的人员的通用用法。与其他示例相比,下面的代码有以下改进:

  • 如果搜索字符串包含任何特殊字符(如括号),则对它们进行转义,以使构造的过滤字符串有效。

  • 仅通过samaccountname搜索就不够了。如果"大卫史密斯"有帐户名称" dsmith",搜索" David"通过samaccountname找不到他。我的示例显示了如何搜索更多字段,并提供了一些可能要搜索的字段。

  • 从类似GC的根开始:"比尝试从Domain.GetComputerDomain()构建LDAP条目更健壮。

  • 必须处理所有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();
                }
            }
        }
    }
    

答案 1 :(得分:4)

虽然在EWS中无法进行通配符搜索,但在AD搜索中是可能的。 AD查询支持通配符。即,可以在AD中搜索* CONF *,这将返回包含“CONF”的所有结果。根据结果​​,查询相应Exchange对象的EWS。您需要找到一个参数,您可以使用该参数找到相应的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查询示例here

答案 2 :(得分:0)

对索引文本字段的模糊搜索只能使用前缀(或后缀...)来完成。这就是为什么Exchange可能会将查询实现为LIKE&#39; CONF%&#39;。

我查看了文档并且没有任何方法可以绕过它 - 全表扫描(%CONF%必须是这种情况)似乎没有意义。

答案 3 :(得分:0)

我一直在尝试使用php-ews从PHP搜索GAL。在网页上,用户开始输入要搜索的名称,一旦输入了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'返回Mike,Michael或Joe,John,除非我为Simon发送'Si'或'si',然后ResolveNames调用从GAL返回前100个条目。

目前我已将输入的最小字符数增加到3,即:'sim',这样可行。问题是当我们让员工只有2个字符的名字时。

我只是想我会分享代码,看看它是否有帮助,看看是否有人知道为什么我的'si'无法正常工作。

我正在访问Exchange 2010