Что лучше для отложенной загрузки навигационной свойства отдельных сущностей с самостоятельным отслеживанием через службу WCF?


У меня есть WCF-клиента, который проходит сущности с самостоятельным отслеживанием в WPF-приложения, построенные с использованием MVVM. Само приложение имеет динамический интерфейс. Пользователи могут выбрать, какие предметы они хотят видимых в зоне их работы в зависимости от того, какую роль они в или какую задачу они выполняют.

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

Мое приложение выглядит так:

[ФОС] <---> [Клиентские Хранилища] <---> [Модель представления] <---> [Просмотр]

Мои модели-это сущности с самостоятельным отслеживанием. Клиентский репозиторий подключается метод LazyLoad (при необходимости) до возвращения модели в ViewModel, который просил его. Все службы WCF звонки asyncronous, что означает LazyLoad методы asyncronous.

Фактическая реализация LazyLoad дает мне некоторые неприятности. Вот варианты, которые пришли мне. Извините за стены текста, они варианты с примерами кода.

Вариант

LazyLoad свойства модели по запросу в Геттер

[DataMember]
public TrackableCollection<ConsumerDocument> ConsumerDocuments
{
    get
    {
        if (_consumerDocuments == null && !IsDeserializing && !IsSerializing)
        {
            _consumerDocuments = new TrackableCollection<ConsumerDocument>();
            _consumerDocuments.CollectionChanged += FixupConsumerDocuments;
            LoadConsumerDocuments();
        }
        return _consumerDocuments;
    }
    set { /* Default T4 STE Set removed for space */ }
}

private async void LoadConsumerDocuments()
{
    var obj = await LazyLoadData("ConsumerDocuments", new object[] { Ident });
    if (obj != null && obj is IEnumerable<ConsumerDocument>)
    {
        ConsumerDocuments.AddRange((IEnumerable<ConsumerDocument>)obj);
    }
}

Хорошо: загрузка данных настолько прост, что не стоит упоминания. Создание привязки в XAML загружает данные, когда это необходимо, и пропускает его, когда он не. Например, однако загрузить данные, если раздел "Документы" интерфейс не существует, то ничего не загружается.

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

public bool HasDocuments 
{ 
    get { return ConsumerDocuments.Count > 0; }
}

ВАРИАНТ Б

Использовать по умолчанию вам для свойства и вручную сделать звонок, чтобы загрузить данные, когда это необходимо

Хорошая: можно использовать эти свойства в любом месте

Плохо: надо помнить, чтобы загрузить данные, прежде чем пытаться получить к нему доступ. Это может показаться простым, но это может выйти из-под контроля быстро. Например, каждый ConsumerDocument имеет UserCreated и UserLastModified. Есть DataTemplate, который определяет UserModel с подсказка дополнительных пользовательских данных, таких как расширение, электронной почты, групп, ролей и т. д. Так что на мой ViewModel, который отображает документы я должен называть LoadDocuments, затем петли через них и называть LoadConsumerModified и LoadConsumerCreated. В основном все, что отображается в пользовательском интерфейсе привязки должен быть загружен в первую очередь.

ВАРИАНТ C

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

// Properties used in Bindings
[DataMember]
public TrackableCollection<ConsumerDocument> ConsumerDocuments
{
    get
    {
        if (_consumerDocuments == null && !IsDeserializing && !IsSerializing)
        {
            _consumerDocuments = new TrackableCollection<ConsumerDocument>();
            _consumerDocuments.CollectionChanged += FixupConsumerDocuments;
            LoadConsumerDocuments();
        }
        return _consumerDocuments;
    }
    set { /* Default T4 STE Set removed for space */ }
}

private async void LoadConsumerDocuments()
{
    var consumerDocuments = await GetConsumerDocuments();
    if (consumerDocuments != null) 
        ConsumerDocuments.AddRange(await GetConsumerDocuments());

    IsConsumerDocumentsLoaded = true;
}

// Method used for non-binding code calls
public async Task<TrackableCollection<ConsumerDocument>> GetConsumerDocuments()
{
    if (IsConsumerDocumentsLoaded)
    {
        return ConsumerDocuments;
    }
    else
    {
        // Do something like call Getter to initialize collection
        if (this.ConsumerDocuments == null) { }

        var obj = await LazyLoadData("ConsumerDocuments", new object[] { Ident });
        if (obj != null && obj is IEnumerable<ConsumerDocument>)
        {
            return new TrackableCollection<ConsumerDocument>((IEnumerable<ConsumerDocument>)obj);
        }
    }
    return null;
}

Хорошо: данные загружаются асинхронно по мере необходимости - именно то, что я хочу.

Плохо: имея два способа получить доступ к одинаковым данным представляется неэффективной и запутанной. Вам нужно помнить, когда вы должны использовать потребитель.GetConsumerDocuments() вместо потребителя.ConsumerDocuments и наоборот. Существует также вероятность того, что вызовов WCF сервис запускается несколько раз.

ВАРИАНТ D

Пропустить загрузку Asyncronous и просто загрузить все синхронно в сеттеров.

Хорошо: очень просто, без дополнительной работы, необходимой

Плохо: блокировка интерфейса при данных нагрузках. Не хочу этого.

ВАРИАНТ Е

Кто-то так и скажи, что есть другой способ сделать это и указать мне на примеры кода :)

Другие Ноты

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

За исключением вручную, называя события загрузки в вариант C, все это может быть сделано с помощью шаблонов T4, чтобы есть очень мало кодирования для меня. Все, что мне нужно сделать, это подключить LazyLoad событие в клиентском хранилище и направьте его на право обслуживания вызовов.



Комментарии
1 ответ

Я опубликовал этот вопрос на так, и есть некоторые хорошие ответы здесь. Ответ я в конце августа был мой собственный, хотя она была основана на некоторых других ответов на вопрос так.

Вот копия ответа я пошла с:


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

Старый Т4 Генерируемые Свойства Навигации

[DataMember]
public MyClass MyProperty { get; set;}

private MyClass _myProperty;

Новый Т4 Генерируемые Свойства Навигации

[DataMember]
internal MyClass MyProperty {get; set;}
public MyClass MyPropertySync {get; set;}
public MyClass MyPropertyAsync {get; set;}

private MyClass _myProperty;
private bool _isMyPropertyLoaded;

private async void LoadMyPropertyAsync();
private async Task<MyClass> GetMyPropertyAsync();
private MyClass GetMyPropertySync();

Я создал три копии объекта, которые указывают на одну и ту же частную собственность. Внутренняя копия для эф. Вероятно, я мог бы избавиться от него, но проще всего просто оставить его в с эф предполагает собственность и ее проще оставить его, чем ремонтировать эф, чтобы использовать новое имя собственность. Он является внутренним, так как я не хочу иметь ничего вне пространства имен класса, чтобы использовать его.

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

Асинхронная версия работает LoadMyPropertyAsync(), который просто запускает GetMyPropertyAsync(). Мне нужно два метода для этого, потому что я не могу поставить асинхронный модификатора на геттер, и мне нужно вернуться в пустоту, если звонить из номера-асинхронного метода.

Синхронизации версия работает GetMyPropertySync() , который в свою очередь запускает GetMyPropertyAsync() синхронно

Поскольку это все Т4 автоматически, не нужно ничего делать, кроме как подключить асинхронный ленивый делегат нагрузки, когда объект получен из службы WCF.

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

<ItemsControl ItemsSource="{Binding CurrentConsumer.DocumentsAsync}" />

CurrentConsumer.DocumentsSync.Clear();


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

5
ответ дан 30 марта 2012 в 02:03 Источник Поделиться