Работа с MS Exchange — I

сложность 100-200

Когда ко мне пришла задача по вытаскиванию вложений из писем с нашего Exchange сервера, я весь внутренне содрогнулся, скривился и постарался откреститься от этой задачи, так как в памяти еще до сих пор свеж опыт работы с VSTO. Я думал, что это будет такой же кромешный ад и страдания в попытке разобраться в логике компонентов, что и для excel и word.

Первые предположения сделали за меня, подумав, что надо будет соединятся с Outlook и через него выдирать сообщения и вложения к ним. К тому письму приложили до кучи пример, мол вот уже все готово, только оформите нам службу красивую. В примере конечно же использовались обертки над COM, с заголовками с структурами работы с С++. С этим языком я работал только в университете и как-то мне не улыбалось снова к нему обращаться, так как не умею на должном уровне. Помимо самой реализации, с которой возникли бы большие проблемы при желании как-то расширить пример, а такие желания возникают обязательно, была проблема безопасности Outlook, который спрашивал разрешения пользователя каждый раз, когда бы служба лезла к ящику. Кроме того реализация не учитывала новые клиенты и Office 2013 шел лесом. В общем, косяков слишком много, чтобы делать что-то серьезное таким способом.

Мир не мог быть так жесток и пришло спасение в виде EWS Managed API от самой MS. Полностью управляемый код для общения с сервером Exchange, что на мой взгляд гораздо лучше для работы с почтой, чем использовать API Outlook.

Кратно о возможностях библиотеки:

  • Возможность создавать, читать, манипулировать всеми пользовательскими элементами сервера (письма, контакты, календарные события, правила).
  • Не требуется пакет офис на целевой машине
  • Автоматический поиск сервера. Т.е. вы вводите только имя пользователя, пароль и имя ящика, все другие необходимые данные для связи библиотека постарается найти сама, в точности как Outlook.
  • NTLM и явная авторизация.

Все подробности по библиотеке и возможности, вы сможете прочитать на официальной странице.

Что будет показано в статье?

Я планирую показать далее следующие техники:

  • Поиск писем и папок
  • Создание и отправка писем. С вложениями.
  • Удаление писем.
  • Ответ и форвардинг писем.
  • Получение писем, уведомления, извлечение вложений.

Установка

Необходимые условия для работы библиотеки:

  • MS Exchange Server 2007 SP1 или новее
  • .Net Framework 3.5 или новее

После того, как вы скачали и установили пакет с библиотеками, в папке drive:\Program Files (x86)\Microsoft\Exchange\Web Services\2.0 появятся все необходимые библиотеки. В GAC они не прописываются, поэтому из стандартного диалога студии на поиск библиотек их видно не будет, но библиотеки подписаны. Стал внимательнее смотреть на этот счет библиотеки за последние полгода-год.

Для работы с проектом, вам лучше всего скопировать их недалеко к проекту и не забыть внести в систему контроля версий. На данный момент, MS не озаботилась официально смастерить и поддерживать nuGet пакет для этого компонента, но добрые люди сделали это за нас. Установить пакет можно командой:

install-Package Microsoft.Exchange.WebServices.lib

В итоге вы получите ту же самую библиотеку, правда без Microsoft.Exchange.WebServices.Auth.dll. Но не думаю, что это сильно расстроит или огорчит вас в большинстве случаев. Для примеров я использовал то, что установилось с официального сайта.

Итак, пусть вы создали проект в студии и успешно подключили библиотеки, теперь можно начинать экспериментировать!

Основные концепции

Перед основной массой экспериментов, я все же хочу рассказать об общих концепциях работы с Exchange Server.

Все операции нацелены на максимальную эффективность в плане объема отсылаемой и обрабатываемой информации. Необходимо стараться минимизировать тот набор данных, который вам нужен для работы, поиска. Только когда вы уверены, что письмо, встреча, или контакт вам нужны, только тогда следует запрашивать полный объект. Например вложения к письму приходят только по специальному запросу.

Тут можно провести некоторую аналогию с базами данных, когда вы работаете с представлениями (View) для того, чтобы получить сокращенный и дополнительно обработанный набор данных, а не все исходные данные. По дополнительному запросу, зная Id записи можно получить всю интересующую информацию.

Основным объектом для работы всегда будет экземпляр класса ExchangeService.

Так же необходимо помнить, что всё сущее в EWS наследуется от класса Item и при результатах поиска следует проверять типы данных и кастовать к требуемому типу.

В целом работа легка и непринужденна, после пары тройки примеров, я думаю, вы уловите основную суть и идею работы с EWS Managed API.

Настройка сервера

Для того, чтобы у вас была возможность работать с MS Exchange Server через EWS, необходимо чтобы была включена служба Exchange Web Services. В настройках Exchange Management Console необходимо проделать следующее  Exchange Management Console -> Mailbox Properties -> Mailbox Features. Проверить что Outlook Web App активирована.

01

Можно это все проверить и по простому: если у вас есть веб доступ к вашему почтовому ящику, то значит все что надо включено и активировано, можно использовать EWS.

Подключение к серверу

Подключение к серверу выполняется очень просто, но есть некоторые нюансы, если ваша программа будет использоваться с разными серверами почты, например при миграции на новую версию.

В стандартном виде подключение к серверу происходит с помощью класса ExchangeService. Да и вообще почти все действия происходят с помощью этого класса. Для подключения необходимо указать версию почтового сервера с которым вы будете работать. Поддерживаются все версии начиная с 2007 SP1.

Для аутентификации можно использовать NTLM, задав свойство UseDefaultCredentials, что автоматом обнулит данные по свойству Credentials, которое требуется задать если вы присоединяетесь от имени другого пользователя, например, у вас есть общий ящик.

Так же, стоит задать использовать метод AutodiscoverUrl(), который принимает имя почтового ящика для получения всех данных по почтовому серверу.

При работе с подключением есть небольшой нюанс: вы не узнаете о проблеме с подключением пока реально не подключитесь к серверу с каким-либо заданием для него. Получается, что надо оборачивать в try{…} catch{…} вызов какого-либо метода и, если попалось исключение, то  пробуем подключиться к другой версии сервера.

В моем случае возможны только две версии сервера, так что я удовлетворился таким решением. Дальнейшее расширение возможно по аналогии.

По итогам всех манипуляций мы получаем работающее соединение с сервером и можем проводить желаемые операции в весьма простом и компактном виде на мой взгляд.

Поиск писем и папок

На мой взгляд стоит сначала разобраться с поиском элементов, так как это даст информацию о том, как построено взаимодействие с сервером вообще.

Я думаю, что первой задачей поставим себе найти письмо от определенного адресата, для этого нам понадобится метод FindItems(). Данный метод 13 перегрузок, так что есть где развернуться.

Самый простой метод для поиска писем по отправителю может выглядеть наверно следующим образом:

Предлагаю разобрать пример построчно. Как правило для поиска используется метод с указанием в какой папке искать, набором критериев для поиска и указанием какое представление для результатов использовать.

Во второй строке примера, сразу за сигнатурой метода идет объявление отображения результатов поиска. В данном случае это дефолтный вывод в 10 записей (задано в конструкторе). Однако мы может задать сдвиг для массива результатов, скажем получить второй десяток записей, указать сортировку записей, указать количество записей на страницу (поясню значение позже), набор параметров отображения, так же можно задать спец атрибут поиска для групп элементов (искать в папке, искать в удаленных, или же использовать ассоциации – только для 2010 и позже. Это видимо сообщения в цепочках).

В третьей строке составляется фильтр для поиска, указывается условие попадания в фильтр, элемент поиска, значение для проверки. Подробнее об этом будет ниже. В данном случае мы хотим, чтобы поле From содержало переданную строку.

Затем идет собственно поиск. Указываем папку, а точнее id папки для поиска из списка стандартных, либо же придется предварительно найти нужную папку. Далее указываем фильтр и то, как будет отображен/сформирован результат.

В цикле разбираем результаты и после проверки, что это действительно письмо, может обращаться к его полям.

Это самый простой вариант, но комплексные варианты поиска, я вас уверяю, не сложнее этого – просто текста больше будет.

Представленный вариант не отличается милосердием к почтовому серверу и запрашивает сразу буквально всю информацию, которая только доступна в письме. Если писем много, то весь этот поток будет забивать вам канал и нагружать сервер. О том, как разгрузить сервер и оптимизировать работу с поиском пойдет дальше речь.

Особенности представления

Загрузка данных

Как я уже написал выше, представления результатов при работе с сервером позволяют ограничить и заранее отсортировать результаты с сервера, чтобы к нам не шли лишние данные.

Например, если при составлении представления мы укажем только поле From, то при формировании сообщения для консоли мы не сможем использовать другие поля и в run-time мы получим исключение.

02

Исключение говорит о том, что мы должны загрузить или назначить данное значение до чтения данных. Назначение может происходить при составлении представления. Но так же можно загружать письмо и отдельно по Id, когда нам нужны крупные данные, например, вложения к письму, но письма мы фильтруем по отправителю.

Теперь исключения не будет, так как мы словно заново получили сообщение с помощью статического метода Bind(), с указанием сервера и номера письма. Данный метод так же может принимать набор свойств, которые надо загрузить.

Сортировка данных

Получаемые данные можно так же сортировать по любому удобному списку полей. При этом список полей для сортировки может и не совпадать со списком полей которые мы получаем с помощью представления.

Сортировка происходит с помощью свойства OrderBy представления.

Таким образом можно добавить любое количество нужных свойств.

Постраничная загрузка данных

Другой полезной возможностью представления является постраничная загрузка данных. Если вы ищите что-то особенное в куче документов, то такой способ может быть очень полезен, так как количество данных переданных клиенту сокращается, так же есть возможность сократить количество работы серверу почты.

Для работы с постраничной загрузкой нам потребуется контролировать самостоятельно сдвиг по результатам, но это не сложно. При создании представления используем перегруженный конструктор для передачи величины страницы, сдвига и базовой точки сдвига, которая определит направление.

У результата есть свойство MoreAvailable по которому можно определить, стоит ли дальше искать данные. Как вы видите сдвиг меняется вручную и весь процесс поиска повторяется. Конечно в реальном коде фильтр нужно вынести за пределы цикла, так как он не меняется.

Продолжение следует…

 

 

Hard’n’Heavy!

 

Оставить комментарий