Как сделать пейджинг с помощью веб-служб Exchange CalendarView

Если я это сделаю:

_calendar = (CalendarFolder)Folder.Bind(_service, WellKnownFolderName.Calendar);

var findResults = _calendar.FindAppointments(
    new CalendarView(startDate.Date, endDate.Date)
);

Иногда я получаю исключение, что слишком много элементов найдено.

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

CalendarView поддерживает конструктор, который позволит мне указать MaxItemsReturned, но я не могу понять, как бы я его повторил, указав offset для подкачки. ItemView имеет этот конструктор:

 public ItemView(int pageSize, int offset)

И использование этого очевидно.

Как насчет CalendarView? Как сделать пейджинг с помощью CalendarView?

Я мог бы сократить диапазон дат до более короткого диапазона, но до сих пор нет способа определить, будет ли он работать точно.

Ответ 1

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

Я играл с некоторыми PowerShell для эмуляции пейджинга, свернув окно CalendarView на основе последнего возвращенного элемента, но, к сожалению, логика расширения CalendarView и Appointment делает невозможным получение именно того, что вам нужно. В основном, поскольку это расширение, оно останавливается на элементах "N", но у вас может быть несколько назначений, которые начинаются в одно и то же время, и это может дать вам один, но не все остальные. Кроме того, любые встречи, которые перекрывают окно, будут включены, поэтому приведенный ниже код перейдет в бесконечный цикл, если у вас было 50 назначений в календаре, все из которых имели одинаковое время начала.

Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll"

$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService
$cred = New-Object Microsoft.Exchange.WebServices.Data.WebCredentials ($user , $passwd)
$service.UseDefaultCredentials = $false
$service.Credentials = $cred
$service.AutodiscoverUrl($user)

$num=50
$total=0
$propsetfc = [Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties
$calfolder = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar

$service.UserAgent = "EWSCalViewTest"
$calview = New-Object Microsoft.Exchange.WebServices.Data.CalendarView("1/1/2012","12/31/2012", $num)
$calview.PropertySet = $propsetfc

do {
    $findresults = $service.FindAppointments($calfolder,$calview)
    write-host  "Found:" $findresults.Items.Count "of" $findresults.TotalCount
    $calview.StartDate = $findresults.Items[$findresults.Items.Count-1].Start
    $total+=$findresults.Items.Count
} while($findresults.MoreAvailable)
write-host $total "total found (including dups)"

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

Если бы мне пришлось писать код с помощью CalendarView, я бы, вероятно, использовал MaxItemsReturned из 1000 (это также предел, который вводит вас в состояние ошибки, если вы не укажете MaxItemsReturned). Если вы получите их всех за один звонок, вы добры. Если вам нужно сделать второй звонок, вам нужно будет сделать дополнительную работу для деления набора результатов. Я также попытался бы ограничить нагрузку на сервер, используя как можно более узкое окно даты в CalendarView, так как вы просите Exchange рассчитать расширение повторяющихся назначений на протяжении всего периода времени. Это может быть довольно дорогостоящей операцией для сервера.

Ответ 2

Вы можете использовать ItemView и SearchFilter для запроса встреч:

var itemView = new ItemView(100, 0);
itemView.PropertySet = new PropertySet(BasePropertySet.IdOnly,
    ItemSchema.Subject, AppointmentSchema.Start, AppointmentSchema.End);

var filter = new SearchFilter.SearchFilterCollection(LogicalOperator.And,
    new SearchFilter.IsEqualTo(ItemSchema.ItemClass, "IPM.Appointment"),
    new SearchFilter.IsGreaterThanOrEqualTo(AppointmentSchema.Start, startDate),
    new SearchFilter.IsLessThan(AppointmentSchema.Start, endDate));

bool moreAvailable = true;
while (moreAvailable)
{
    var result = _service.FindItems(WellKnownFolderName.Calendar, filter, itemView);

    foreach (var appointment in result.OfType<Appointment>())
    {
        DateTime start = appointment.Start;
        DateTime end = appointment.End;
        string subject = appointment.Subject;

        // ...
    }

    itemView.Offset += itemView.PageSize;
    moreAvailable = result.MoreAvailable;
}

Ответ 3

Вы все еще можете разбивать на страницы функцию FindAppointments, управляющую датами начала CalendarView.

var cal = CalendarFolder.Bind(_service, WellKnownFolderName.Calendar);
var cv = new CalendarView(start, end, 1000);

var appointments = new List<Appointment>();

var result = cal.FindAppointments(cv);

appointments.AddRange(result);

while (result.MoreAvailable)
{
     cv.StartDate = appointments.Last().Start;

     result = cal.FindAppointments(cv);

     appointments.AddRange(result);
}

Хотя я не знаю, приходят ли они в порядок. Если это не так, вам, возможно, придется использовать последнюю дату запуска и удалить дубликаты.