Silverlight, DataPager, RIA Services и интеллектуальный пейджинг

Я все еще пытаюсь встать на ноги с Silverlight и RIA Services, и, конечно же, начинаю с некоторых более "забавных" вещей, таких как решетки и интеллектуальная подкачка страниц. Я могу подключиться к службам RIA (используя собственно ORM, а не L2S или EF), получить данные в сети и подключиться к DataPager. Доменная служба работает хорошо с доморощенным ORM, по крайней мере, для запросов. (Все еще работает над полным CRUD.) Однако, все еще есть проблемы:

  1. Для поддержки пользовательского приложения мне нужна управляемая пользователем сортировка и фильтрация, в дополнение к интеллектуальному разбиению по страницам (запускайте запрос только для строк, которые необходимо отобразить) и группировку.

  2. До сих пор я не видел в DataGrid или DataPager ничего такого, чтобы экстернализировать эти возможности, чтобы параметры фильтрации, сортировки и подкачки можно было передавать на сервер для создания соответствующего запроса.

  3. Наборы данных потенциально довольно большие; моя таблица, которую я выбрал для работы с прототипами, может содержать до 35 000 записей у некоторых клиентов, и я уверен, что есть другие таблицы, намного большие, с которыми мне придется иметь дело в какой-то момент. Таким образом, аспект "умного пейджинга" имеет важное значение.

Мы приветствуем идеи, предложения, советы и полезные советы.

3 ответа

Решение

Хорошо, я провел несколько дней в сорняках с этим, и я думаю, что справлюсь с этим.

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

Элемент управления Silverlight Datapager имеет свойство ItemCount. Он доступен только для чтения и не может быть привязан к данным в XAML или установлен непосредственно в коде. Однако если пользовательский элемент управления, содержащий пейджер, имеет DataContext, который реализует IPagedCollectionView, тогда объект контекста данных должен реализовать свойство ItemCount с уведомлением PropertyChanged, и DataPager, похоже, подхватывает это автоматически.

Во-вторых, я настоятельно рекомендую серию великолепных постов Брэда Абрамса в RIA Services, особенно эту - на ViewModel. Он содержит большую часть того, что вам нужно, чтобы пейджинг и фильтрация работали, хотя в нем отсутствует важная часть управления количеством элементов. Его загружаемый образец также содержит очень хорошую базовую среду для реализации ModelViewViewModel (MVVM). Спасибо, Брэд!

Итак, вот как заставить работать счетчик предметов. (Этот код относится к пользовательскому ORM, в то время как код Брэда использует Entity Framework; между ними вы можете понять, что вам нужно в вашей среде.)

Во-первых, ваш ORM должен поддерживать получение количества записей, с вашим фильтром и без него. Вот мой код службы домена, который делает счет доступным для RIA Services:

[Invoke]
public int GetExamCount()
{
    return Context.Exams.Count();
}

[Invoke]
public int GetFilteredExamCount(string descriptionFilter)
{
    return Context.Exams.GetFilteredCount(descriptionFilter);
}

Обратите внимание на атрибут [Invoke]. Это необходимо для любого метода DomainService, который не возвращает сущность или коллекцию сущностей.

Теперь для кода ViewModel. Вам нужен ItemCount, конечно. (Это из примера Брэда.)

    int itemCount;
    public int ItemCount
    {
        get { return itemCount; }
        set
        {
            if (itemCount != value)
            {
                itemCount = value;
                RaisePropertyChanged(ItemCountChangedEventArgs);
            }
        }
    }

Ваш метод LoadData запустит запрос, чтобы получить текущий набор строк для отображения в DataGrid. (Это пока не реализует пользовательскую сортировку, но это простое дополнение.)

    EntityQuery<ExamEntity> query = 
        DomainContext.GetPagedExamsQuery(PageSize * PageIndex, PageSize, DescriptionFilterText);
    DomainContext.Load(query, OnExamsLoaded, null);

Затем метод обратного вызова запускает запрос, чтобы получить количество. Если фильтр не используется, мы получаем количество для всех строк; если есть фильтр, то мы получаем количество отфильтрованных строк.

private void OnExamsLoaded(LoadOperation<ExamEntity> loadOperation)
{
    if (loadOperation.Error != null)
    {
        //raise an event... 
        ErrorRaising(this, new ErrorEventArgs(loadOperation.Error));
    }
    else
    {
        Exams.MoveCurrentToFirst();
        if (string.IsNullOrEmpty(DescriptionFilterText))
        {
            DomainContext.GetExamCount(OnCountCompleted, null);
        }
        else
        {
            DomainContext.GetFilteredExamCount(DescriptionFilterText, OnCountCompleted, null);
        }
        IsLoading = false;
    }
}

Есть также метод обратного вызова для подсчета:

void OnCountCompleted(InvokeOperation<int> op)
{
    ItemCount = op.Value;
    TotalItemCount = op.Value;
}

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

LINQ облегчает запрос с помощью.Skip() и.Take(). Делать это с сырым ADO.NET сложнее. Я узнал, как это сделать, разобрав запрос, сгенерированный LINQ.

SELECT * FROM 
    (select ROW_NUMBER() OVER (ORDER BY Description) as rownum, * 
     FROM Exams as T0  WHERE T0.Description LIKE @description ) as T1 
WHERE T1.rownum between @first AND @last ORDER BY rownum

Интересным является предложение "выберите ROW_NUMBER() OVER (ORDER BY Description) в качестве rownum", потому что еще не многие люди используют "OVER". Этот пункт сортирует таблицу по описанию до назначения номеров строк, и фильтр также применяется до назначения номеров строк. Это позволяет внешнему SELECT фильтровать номера строк после сортировки и фильтрации.

Вот и все, умный пейджинг с фильтрацией в RIA Services и Silverlight!

Вот быстрое и грязное решение (которое я выбрал):

Просто переместите DomainDataSource в вашу ViewModel! Готово!

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

Внутри вашей ViewModel просто создайте экземпляр источника данных:

// Feedback DataSource
_dsFeedback = new DomainDataSource();
_dsFeedback.DomainContext = _razorSiteDomainContext;
_dsFeedback.QueryName = "GetOrderedFeedbacks";
_dsFeedback.PageSize = 10;
_dsFeedback.Load();

и предоставить привязываемое свойство:

private DomainDataSource _dsFeedback { get; set; }
public DomainDataSource Feedback 
{
    get 
    {
        return _dsFeedback;
    }
}

И добавьте ваш DataPager в ваш XAML:

  <data:DataPager Grid.Row="1"
                  HorizontalAlignment="Stretch" 
                  Source="{Binding Feedback.Data}" 
                  Margin="0,0,0,5" />

  <data:DataGrid ItemsSource="{Binding Feedback.Data}">


PS. Благодаря "Франсуа" с вышеупомянутой связанной страницы. Я даже не осознавал, что могу вынуть DomainDataSource из XAML, пока не увижу ваш комментарий!

Это интересная статья от мая 2010 года о возможной будущей поддержке этого типа функций в платформе.

http://www.riaservicesblog.net/Blog/post/WCF-RIA-Services-Speculation-EntityCollectionView.aspx

Другие вопросы по тегам