Question

Je suis création d'une application, qui utilise les entités du LoadOperation pour retourner un IEnumerable qui devient la source d'un CollectionViewSource dans ma vue Modèle. Je découvre maintenant l'écueil potentiel de cette approche, lors de l'ajout des entités dans mon client Silverlight, je ne vois pas ces entités, à moins que je soumets soit la nouvelle entité, puis recharger ou maintenir une collecte séparée des articles que je suis se liant à.

Ce que je vois vraiment que mes options sont:

  1. Ajoutez un ObservableCollection à utiliser comme source de la propriété CollectionViewSource dans mon ViewModel - de cette façon que je peux ajouter à la fois la DomainContext et la ObservableCollection en même temps de garder les collections en synchronisation
  2. .
  3. Changer la liaison au EntitySet directement, et ajouter un gestionnaire d'événements de filtrage pour fournir le filtrage sur le CollectionViewSource.

Si quelqu'un a des conseils ou des idées sur les avantages / inconvénients de chacun, je serais très heureux. En particulier, je me demande, s'il y a des avantages de performance et / ou de programmation en faveur de l'un ou l'autre?

Était-ce utile?

La solution

Je prends cette seule approche à la fois. Tout d'abord, je vais montrer un point de référence à ce dicuss avec, alors je vais mettre en évidence les différents changements nécessaires pour soutenir chaque méthode.

La base de ma démo est un service de domaine unique, authentifié qui retourne une seule entité de ressources. Je vais exposer 4 commandes (enregistrer, annuler, ajouter et supprimer), plus une collection et une propriété de tenir le SelectedResource.

2 Différentes classes de mettre en œuvre cette interface (1 pour le mélange, 1 pour la production). La production est le seul que je vais discuter ici. Notez l'action (lo.Entities) dans la fonction GetMyResources:

public class WorkProvider
{
    static WorkContext workContext;
    public WorkProvider()
    {
        if (workContext == null)
            workContext = new WorkContext();
    }
    public void AddResource(Resource resource)
    {
        workContext.Resources.Add(resource);
    }
    public void DelResource(Resource resource)
    {
        workContext.Resources.Remove(resource);
    }
    public void UndoChanges()
    {
        workContext.RejectChanges();
    }
    public void SaveChanges(Action action)
    {
        workContext.SubmitChanges(so =>
            {
                if (so.HasError)
                    // Handle Error
                    throw so.Error;
                else
                    action();
            }, null);
    }
    public void GetMyResources(Action<IEnumerable<Resource>> action)
    {
        var query = workContext.GetResourcesQuery()
            .Where(r => r.UserName == WebContext.Current.User.Name);
        workContext.Load(query, LoadBehavior.MergeIntoCurrent, lo =>
            {
                if (lo.HasError)
                    // Handle Error
                    throw lo.Error;
                else
                    action(lo.Entities);
            }, null);
    }
}

Dans le ViewModel, je la mise en œuvre suivante:

public class HomeViewModel : ViewModelBase
{
    WorkProvider workProvider;
    public HomeViewModel()
    {
        workProvider = new WorkProvider();
    }

    // _Source is required when returning IEnumerable<T>
    ObservableCollection<Resource> _Source; 
    public CollectionViewSource Resources { get; private set; }
    void setupCollections()
    {
        Resources = new CollectionViewSource();
        using (Resources.DeferRefresh())
        {
            _Source = new ObservableCollection<Resource>();
            Resources.Source = _Source;
            Resources.GroupDescriptions.Add(new PropertyGroupDescription("Title"));
            Resources.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
            Resources.SortDescriptions.Add(new SortDescription("Rate", ListSortDirection.Ascending));
        }
    }
    void loadMyResources()
    {
        workProvider.GetMyResources(results =>
            {
                using (Resources.DeferRefresh())
                {
                    // This is required when returning IEnumerable<T>
                    _Source.Clear();
                    foreach (var result in results)
                    {
                        if (!_Source.Contains(result))
                            _Source.Add(result);
                    }
                }
            });
    }
    Resource _SelectedResource;
    public Resource SelectedResource
    {
        get { return _SelectedResource; }
        set
        {
            if (_SelectedResource != value)
            {
                _SelectedResource = value;
                RaisePropertyChanged("SelectedResource");
            }
        }
    }

    public RelayCommand CmdSave { get; private set; }
    public RelayCommand CmdUndo { get; private set; }
    public RelayCommand CmdAdd { get; private set; }
    public RelayCommand CmdDelete { get; private set; }
    void setupCommands()
    {
        CmdSave = new RelayCommand(() =>
            {
                workProvider.SaveChanges(() =>
                    {
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                            {
                                System.Windows.MessageBox.Show("Saved");
                            });
                    });
            });
        CmdUndo = new RelayCommand(() =>
            {
                workProvider.UndoChanges();
                // This is required when returning IEnumerable<T>
                loadMyResources();
            });
        CmdAdd = new RelayCommand(() =>
            {
                Resource newResource = new Resource()
                {
                    ResourceID = Guid.NewGuid(),
                    Rate = 125,
                    Title = "Staff",
                    UserName = "jsmith"
                };
                // This is required when returning IEnumerable<T>
                _Source.Add(newResource);
                workProvider.AddResource(newResource);
            });
        CmdDelete = new RelayCommand(() =>
        {
            // This is required when returning IEnumerable<T>
            _Source.Remove(_SelectedResource);
            workProvider.DelResource(_SelectedResource);
        });
    }
}

La méthode alternative consisterait à changer la classe WorkProvider comme suit (notez l'action (workContext.Resources) qui est retourné:

    public void GetMyResources(Action<IEnumerable<Resource>> action)
    {
        var query = workContext.GetResourcesQuery()
            .Where(r => r.UserName == WebContext.Current.User.Name);
        workContext.Load(query, LoadBehavior.MergeIntoCurrent, lo =>
            {
                if (lo.HasError)
                    // Handle Error
                    throw lo.Error;
                else
                    // Notice Changed Enumeration
                    action(workContext.Resources);
            }, null);
    }

Et les modifications apportées à la viewmodel sont les suivants (notez la suppression du _Source ObservableCollection):

public class HomeViewModel : ViewModelBase
{
    WorkProvider workProvider;
    public HomeViewModel()
    {
        workProvider = new WorkProvider();
    }

    public CollectionViewSource Resources { get; private set; }
    void setupCollections()
    {
        Resources = new CollectionViewSource();
        using (Resources.DeferRefresh())
        {
            Resources.Filter += (s,a) =>
                {
                    a.Accepted = false;
                    if (s is Resource)
                    {
                        Resource res = s as Resource;
                        if (res.UserName == WebContext.Current.User.Name)
                            a.Accepted = true;
                    }
                };
            Resources.GroupDescriptions.Add(new PropertyGroupDescription("Title"));
            Resources.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));
            Resources.SortDescriptions.Add(new SortDescription("Rate", ListSortDirection.Ascending));
        }
    }
    void loadMyResources()
    {
        workProvider.GetMyResources(results =>
            {
                using (Resources.DeferRefresh())
                {
                    Resources.Source = results;
                }
            });
    }
    Resource _SelectedResource;
    public Resource SelectedResource
    {
        get { return _SelectedResource; }
        set
        {
            if (_SelectedResource != value)
            {
                _SelectedResource = value;
                RaisePropertyChanged("SelectedResource");
            }
        }
    }

    public RelayCommand CmdSave { get; private set; }
    public RelayCommand CmdUndo { get; private set; }
    public RelayCommand CmdAdd { get; private set; }
    public RelayCommand CmdDelete { get; private set; }
    void setupCommands()
    {
        CmdSave = new RelayCommand(() =>
            {
                workProvider.SaveChanges(() =>
                    {
                        DispatcherHelper.CheckBeginInvokeOnUI(() =>
                            {
                                System.Windows.MessageBox.Show("Saved");
                            });
                    });
            });
        CmdUndo = new RelayCommand(() =>
            {
                workProvider.UndoChanges();
                Resources.View.Refresh();
            });
        CmdAdd = new RelayCommand(() =>
            {
                Resource newResource = new Resource()
                {
                    ResourceID = Guid.NewGuid(),
                    Rate = 125,
                    Title = "Staff",
                    UserName = "jsmith"
                };
                workProvider.AddResource(newResource);
            });
        CmdDelete = new RelayCommand(() =>
        {
            workProvider.DelResource(_SelectedResource);
        });
    }
}

Alors que la deuxième approche requiert certainement ajouter le gestionnaire d'événements de filtre dans la configuration de la CollectionViewSource, et pourrait être considéré comme des données de filtrage 2 fois (1 fois au niveau du serveur, et la deuxième fois par le CollectionViewSource), il ne hors avantages suivants: Il y a une collection unique - ce qui rend la gestion des notifications de collecte plus simple et plus facile. La collection est la collection réelle qui sera soumis au serveur, ce qui rend la gestion ajoute / supprime plus simple, car il n'y a pas de possibilités pour oublier d'ajouter / supprimer des entités de la collection correcte pour lancer la liste Ajout / suppression fonction lors de la présentation de retour.

L'une dernière chose que je dois confirmer est la suivante: Sur un CollectionViewSource, je crois comprendre que vous devez utiliser DeferRefresh () lors de multiples changements qui affectent la vue. Cette juste empêche inutiles rafraîchit lorsque des changements survenant internes peuvent provoquer des rafraîchissements tels que la configuration de tri, le regroupement, etc. Il est également important d'appeler .View.Refresh () lorsque nous nous attendons à l'interface utilisateur pour traiter des changements de mise à jour. Le .View.Refresh () est probablement plus important de noter que le DeferRefresh (), car elle provoque en fait une mise à jour de l'interface utilisateur, par opposition à une mise à jour de l'interface utilisateur empêche inattendues.

Je ne sais pas si cela va aider les autres, mais je l'espère. Je certainement passé un certain temps de travail à travers ceux-ci et d'essayer de comprendre. Si vous avez des éclaircissements ou d'autres choses à ajouter, s'il vous plaît ne hésitez pas à le faire.

Autres conseils

Ryan, il pourrait être utile de votre temps pour jeter un coup d'oeil par ce post sur la collecte de liaison (et certains de ceux liés). Votre mise en œuvre est certainement raisonnable, mais je peux le voir se bat avec quelques-unes des questions qui ont déjà été résolus au niveau du cadre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top