Silverlight, DataPager, RIA Services и интеллектуальная подкачка

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

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

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

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

Приветствуются идеи, предложения, рекомендации и кирпичи nerf.

Ответы

Ответ 1

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

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

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

Во-вторых, я очень рекомендую отличную= "http://blogs.msdn.com/brada/archive/2009/08/02/business-apps-example-for-silverlight-3-rtm-and-net-ria-services-july-update-summary.aspx" rel= "noreferrer" серию блога Brad Abrams в службах RIA, особенно на ViewModel. Он содержит большую часть того, что вам нужно для работы подкачки и фильтрации, хотя он не имеет критической части для управления количеством элементов. Его загружаемый образец также содержит очень хорошую базовую структуру для реализации ModelViewViewModel (MVVM). Спасибо, Брэд!

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

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

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

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

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

Теперь для кода 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;
    }
}

Также существует метод обратного вызова для count:

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

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

LINQ упрощает запрос с помощью .Skip() и .Take(). Выполнение этого с использованием raw 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

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

Таким образом, это интеллектуальный пейджинг с фильтрацией в службах RIA и Silverlight!

Ответ 2

Здесь быстрое и грязное решение (за которое я пошел):

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

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

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

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

и предоставить свойство bindable:

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, пока не увижу ваш комментарий!