Pergunta

Eu tenho construído um aplicativo, que usa as entidades da LoadOperation para devolver um IEnumerable, que se torna a fonte de uma Coleção ViewSource no meu modelo de visualização. Agora estou descobrindo a possível armadilha para essa abordagem, ao adicionar entidades no meu cliente Silverlight, não consigo ver essas entidades, a menos que envie a nova entidade, depois recarregue ou mantenha uma coleção separada de itens, aos quais estou vinculado.

O que eu realmente vejo como minhas opções são:

  1. Adicione uma observação ObservableCollection a ser usada como fonte da propriedade Coleção ViewSource no meu ViewModel - dessa maneira posso adicionar ao domainContext e à ObservableCollection ao mesmo tempo para manter as coleções sincronizadas.
  2. Altere diretamente a ligação ao EntitySet e adicione um manipulador de eventos de filtragem para fornecer a filtragem no Coleção ViewSource.

Se alguém tiver dicas ou pensamentos sobre prós/contras de cada um, eu apreciaria muito. Em particular, estou me perguntando, se há benefícios de desempenho e/ou programação em favor de um ou outro?

Foi útil?

Solução

Estou adotando essa abordagem de cada vez. Primeiro, vou mostrar um ponto de referência à DiCUSS com isso, então destacarei as diferentes mudanças necessárias para apoiar cada metodologia.

A base para minha demonstração é um único serviço de domínio autenticado, que retorna uma única entidade de recurso. Exporei 4 comandos (salvar, desfazer, adicionar e excluir), além de uma coleção e uma propriedade para manter o SelectedResource.

2 classes diferentes implementam essa interface (1 para mistura, 1 para produção). A produção é a única que discutirei aqui. Observe a ação (lo.entities) na função 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);
    }
}

No ViewModel, tenho a seguinte implementação:

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);
        });
    }
}

O método alternativo envolveria a alteração da classe WorkProvider da seguinte forma (observe a ação (WorkContext.resources) que é devolvida:

    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);
    }

E as mudanças no ViewModel são as seguintes (observe a remoção da _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);
        });
    }
}

Embora a segunda abordagem definitivamente exija a adição do manipulador de eventos do filtro na configuração do ColeçãoViewSource, e possa ser visto como dados de filtragem 2 vezes (1 tempo no servidor e a segunda vez pelo ColeçãoViewSource), ela refere os seguintes benefícios: Há uma única coleção - que torna o gerenciamento das notificações de coleta mais simples e fácil. A coleção é a coleção real que será enviada ao servidor, o que simplifica o gerenciamento/exclusão mais simples, pois não há oportunidades para esquecer de adicionar/remover entidades da coleção correta para iniciar a função Adicionar/excluir ao enviar novamente.

A última coisa que preciso confirmar é a seguinte: em uma Coleção ViewSource, é meu entendimento de que você deve usar o adjingRefresh () ao fazer várias alterações que afetam a visualização. Isso apenas impede que as atualizações desnecessárias ocorram quando alterações internas podem causar atualizações como configurar a classificação, o agrupamento etc. Também é importante chamar .View.Refresh () quando esperamos que a interface do usuário processe algumas alterações de atualização. O .View.Refresh () é provavelmente mais importante para observar do que o adjustefresh (), pois realmente causa uma atualização da interface do usuário, em oposição a uma atualização inesperada da interface do usuário.

Não sei se isso ajudará os outros, mas espero que sim. Definitivamente, passei algum tempo trabalhando com isso e tentando entender isso. Se você tem esclarecimentos ou outras coisas a acrescentar, sinta -se à vontade para fazê -lo.

Outras dicas

Ryan, pode valer a pena dar uma olhada Esta postagem na coleta de coleção (e alguns dos relacionados). Sua implementação é certamente razoável, mas posso vê -la lutar com alguns dos problemas que já foram resolvidos no nível da estrutura.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top