Architecture Silverlight LOB – Concepts et technologies (Partie 7)

Dans ce poste, je vais parler de la construction d’un framework MVVM.

Comme je le disais dans le poste précédent, ce poste devrait être sur le OrderView. Ce view devrait permettre aux utilisateurs de choisir des éléments d’une liste de produits, les ajouter à l’ordre courant, choisir les quantités, voir le prix courant total et soumettre la commande. En outre, l’utilisateur doit être en mesure de filtrer, trier et avoir une pagination sur la liste des produits. Que devient le produit choisi, ajoutez-le à une autre collection ou de créer un détail de la commande fondé sur lui, puis mettre à jour certaines autres données sur l’UI et, enfin, soumettre les modifications sur le serveur. Le truc, c’est que tout le monde a sa propre façon de programmation et de la sorte quand vous vous retrouvez dans une équipe, vous pouvez constater que deux personnes code la même chose d’une manière différente, et on a un bug dans la situation A et l’autre a un bug dans la situation B. Ayant un bon cadre MVVM avec une méthodologie bien définie est indispensable pour prévenir ces situations. Dans ce poste, je veux parler des éléments essentiels que vous devez avoir dnas un bon cadre MVVM. Plus tard, je vais vous décrire un cadre MVVM sur lequel je travaille qui était basée sur WCF RIA Services, mais ne dépend pas vraiment de ??ça.

Depuis que nous suivons les meilleures pratiques, nous savons que l’utilisation d’une architecture MVVM bien que nous pouvons arriver à une solution dont l’extraction, le filtrage, le tri, la logique d’échange est entièrement séparé du view, ce qui nous permet d’avoir aussi des vues différentes pour le même view model. Par exemple, nous pouvons commencer avec l’aide d’un DataGrid et un DataPager par afficher nos articles, mais plus tard, fournir une vision nouvelle qui utilise les comboboxes pour sélectionner les options de tri, une zone de liste d’albums pour afficher les éléments et les boutons personnalisés pour la pagination. En outre, nous devrions être en mesure de séparer toute cette logique de sa logique d’accès aux données réelles pour pouvoir utiliser des objets fantaisie de notre modèle et de faire des tests unitaires pour nos ViewModels. Ce n’est pas une tâche facile mais c’est ce que je veux réaliser à partir de maintenant.

Eh bien, pour commencer,.NET/Silverlight nous offre déjà quelques classes et interfaces qui sont très pratiques pour les scénarios de MVVM.

¦ INotifyPropertyChanged – Utilisé pour déclencher un événement lors d’un changement de propriété. WPF/Silverlight binding framework : utilise cette interface pour mettre à jour la vue lors d’un changement de propriété.

¦ INotifyCollectionChanged – Utilisé pour déclencher un événement lorsque l’insérer, supprimer, effacer ou remplacer l’opération a été fait contre une collection. Les contrôles WPF/Silverlight qui ont une propriété ItemsSource utilisent généralement cette interface pour créer ou supprimer des éléments visuels dans un container. Par exemple, ListBoxes affiche de nouvelle ListBoxItems, DataGrids affiche de nouvelle DataGridRows.

¦ ICollectionView – Utilisé pour fournir des descriptions sorte de filtre, des descriptions de groupe, et la sélection de l’objet pour une collection IEnumerable et ont la vue d’afficher uniquement les éléments filtrés, triés selon les descriptions de tri et de mettre en évidence l’élément sélectionné. (a plus de fonctionnalités, mais ce sont les plus pertinentes pour le bien de ce poste).

¦ IPagedCollectionView – Utilisé pour fournir des options de pagination à une collection IEnumerable. Il est utilisé par la plupart DataPagers, qui font des appels à la MoveToPage (pageIndex int) la méthode et nous permet de s’inscrire à l’événement PageChanging chercher une nouvelle page d’entités à afficher.

¦ Il existe d’autres interfaces importantes comme IEditableObject, IEditableCollectionView mais je ne vais pas les couvrir dans ces postes. Ils sont utilisés pour mettre à jour les valeurs des propriétés d’un objet de façon atomique.

Malheureusement, WCF RIA Services collections n’appliquent pas ICollectionView ou IPagedCollectionView si nous devons fournir notre propre mécanisme de filtrage, de tri et de pagination des données. Toutefois, cela est assez facile à faire avec LINQ et des expressions et nous permet de composer des requêtes avec certains opérateurs de LINQ avec Silverlight. Silverlight a sa propre implémentation de IPagedCollectionView, appelé System.Windows.Data.PagedCollectionView mais il a été conçu pour fonctionner avec les collections en mémoire et non pas côté serveur « collections ». Dans cette série, je suis mon propre fournisseur IPagedCollectionView / mise en Å“uvre ICollectionView que j’ai conçu pour fonctionner avec tout type de sources de données. Quelle que soit l’option utilisée est entièrement au modèle de MVVM.

Donc, voyant voir comment mon framework MVVM fonctionne.

Tout d’abord, nous avons le concept de modèle:

public interface IDomainModel : IDisposable
{
    void RejectChanges();
    ISubmitOperation SaveChanges();
    ISubmitOperation SaveChanges(Action<ISubmitOperation> callback, object userState);
 
    IDomainQuery<T> GetQuery<T>();
 
    void Add<T>(T entity);
    void Remove<T>(T entity);
    void Attach<T>(T entity);
    void Detach<T>(T entity);
 
    bool HasChanges { get; }
    bool IsSubmitting { get; }
    bool IsLoading { get; }
}

Si vous êtes familier avec l’API DomainContext, vous trouverez cette interface très facile à comprendre. Cette interface modèle est censé vous fournir des fonctionnalités de base pour la récupération de données, de le (modifier et l’enregistrer. Pour des opérations plus spécifiques, vous pouvez étendre cette interface, ajouter des opérations spécifiques et vous avez votre ViewModel en dépendent.

L’interface IDomainQuery représente une requête sur un type d’entité certaines référentiel (quelle qu’elle soit). Comme une requête, il vous permet de filtrer, trier et la page de données.

public interface IDomainQuery : IDisposable
{
    void Cancel();
    void Load();
 
    event Action Loading;
    event Action<ILoadOperation> Loaded;
    event Action Canceled;
}
 
public interface IDomainQuery<T> : IDomainQuery
{
    /// <summary>
    /// Delegate that returns a lambda expression to apply in a Where operation
    /// </summary>
    Func<Expression<Func<T, bool>>> FilterExpression { get; set; }
 
    /// <summary>
    /// SortDescription values that are applied in a OrderBy/ThenBy operation
    /// </summary>
    Func<SortDescriptionCollection> SortDescriptions { get; set; }
 
    /// <summary>
    /// Contains the required data that are applied in a Skip and Take operation
    /// </summary>
    Func<PageDescription> PageDescription { get; set; }
 
    new event Action<ILoadOperation<T>> Loaded;
}
 
public sealed class PageDescription
{
    public int PageSize { get; set; }
    public int PageIndex { get; set; }
}

En outre, il a des événements pour vous permettre de savoir quand une operation a commencé et terminé. Il ya deux choses importantes à noter dans l’interface IDomainQuery. La premiere est le fait que la façon dont vous filtrer une IDomainQuery est de savoir comment vous filtrez une RIA Services EntityQuery et comment vous filtrez un objet IQueryable. Cela signifie qu’il est très facile à faire implémentations IDomainQuery que les dépôts de requête en mémoire ou des référentiels côté serveur (noter que EntityQuery ne pas mettre en Å“uvre IQueryable et ce n’est pas facile d’intégrer les deux concepts). L’autre chose importante est que le filtre, de tri, le mécanisme de la page est basée sur les délégués qui signifie que chaque fois que la requête est exécutée / réexécutée ces délégués sont toujours réévalués et seront toujours le reflet de votre état ​​actuel de ViewModel.

J’ai créé des interfaces pour des opérations de « load » et de « submit ». J’ai mis en Å“uvre toutes ces interfaces avec des classes qui comptent sur ​​Silverlight.

Si vous êtes familier avec DomainContext Vous êtes en train de vous demandez sans doute où sont les EntitySets et où avez-vous manipuler des collections. Eh bien, depuis ma mise en Å“uvre s’appuie sur WCF IDomainModel RIA Services, vous pouvez étendre cette classe et de manipuler votre DomainContext à mettre en Å“uvre toute opération que vous souhaitez mettre à disposition dans votre étendu IDomainModel. Mais ce cadre ne sont pas censés être consulté dans votre ViewModel. Au lieu de cela, j’utilise mon application ICollectionView, que j’ai appelé DomainCollectionView.

public interface IDomainCollectionView : ICollectionView, IPagedCollectionView, INotifyPropertyChanged
{
    void Add(object item);
    void Remove(object item);
}
 
public interface IDomainCollectionView<T> : IDomainCollectionView, IEnumerable<T>
{
    void Add(T item);
    void Remove(T item);
}

l’implementation actuelle repose sur une IDomainQuery et sera en vue pour les résultats qui sont récupérées dans chaque opération. Comme vous pouvez mettre en Å“uvre un IDomainQuery pour toute IEnumerable vous pouvez utiliser cette mise en Å“uvre de toute source de données.

public class DomainCollectionView<T> : ViewModelBase, IDomainCollectionView<T>
{
    private IDomainQuery<T> query;
    private ObservableCollection<T> sourceCollection;
 
    #region DomainCollectionView Members
 
    public DomainCollectionView(IDomainQuery<T> query, bool usePaging = true)
    {
        this.query = query;
        this.sourceCollection = this.CreateSourceCollection();
        this.CurrentItem = null;
        this.CurrentPosition = -1;
 
        //Apply sort and paging
        query.SortDescriptions = () => this.SortDescriptions;
        if (usePaging)
        {
            this.canChangePage = true;
            this.pageSize = 10;// default page size to 10
            query.PageDescription = () => new PageDescription { PageSize = this.PageSize, PageIndex = this.PageIndex };
        }
 
        query.Loaded += this.OnDataRefreshed;
    }
 
    private void OnDataRefreshed(ILoadOperation<T> loadOp)
    {
        using (this.DeferRefresh())
        {
            this.sourceCollection.Clear();
            foreach (var entity in loadOp.Entities)
                this.sourceCollection.Add(entity);
 
            this.ItemCount = loadOp.TotalEntityCount;
            this.TotalItemCount = loadOp.TotalEntityCount;
        }
    }
 
    //...
}

Maintenant que j’ai introduit le modèle et le IDomainCollectionView, il est temps que nous parlions un peu de la ViewModel.

Contrairement au modèle, ViewModels ne sont pas beaucoup réutilisables, car ils dépendent généralement sur les besoins de la vue. Cependant, beaucoup de view point dans les applications métier ont des besoins communs: la recherche/listing des entités, édition des entités, créer des associations entre entités. Nous pouvons créer des ViewModels à partir de ces concepts et de les étendre seulement et les implementer à la logique métier. Toutes les infrastructures et la plomberie peuvent être centralisés et réutilisés partout.

public class ListVM<T> : ViewModelBase, IListVM<T>
{
    private IDomainModel model;
 
    public ListVM() { }
 
    protected virtual IDomainModel CreateModel()
    {
        return ServiceLocator.Current.GetInstance<IDomainModel>();
    }
 
    public void Initialize(IListVMInitializeArgs args)
    {
        this.model = args.ExistingModel ?? this.CreateModel();
        var query = model.GetQuery<T>();
        this.Entities = new DomainCollectionView<T>(query, args.RequiresPaging);
        query.Load();
    }
 
    public IDomainCollectionView<T> Entities
    {
        get;
        private set;
    }
}

Avec juste quelques lignes de code, nous avons une base du ViewModel qui permet :

■ La visualisation des résultats d’une requête qui peuvent être triés et paginée sur le serveur. Juste lier un datagrid à une collection d’entités et jouer avec lui.
■ La réutilisation d’un modèle existant ou en créer un nouveau. L’implémentation actuelle peut utiliser un modèle commun de toute l’application qui est enregistré dans le conteneur Dependency Injection actuelle ou laisser un ViewModel dérivés décider quoi faire.

A bientôt,

Bonne programmation.

PS : cet article est une adaptation en français d’une suite d’articles par Manuel Felício

Kamel DJELLAL
Chef de projet
EDIS CONSULTING – GROUPE UBISIDE

http://www.ubiside.fr/

Laisser un commentaire