Qual a melhor maneira de conectar o contexto do banco de dados do Entity Framework (modelo) ao ViewModel no MVVM WPF?

9

Como na pergunta acima: Qual a melhor maneira de conectar o modelo de banco de dados do Entity Framework (contexto) ao viewModel no MVVM (WPF)?

Estou aprendendo o padrão MVVM no WPF, muitos exemplos mostram como implementar o modelo no viewModel, mas os modelos nesses exemplos são apenas classes simples, quero usar o MVVM junto com o modelo de estrutura de entidade (abordagem básica de primeira). Qual é a melhor maneira de conectar o modelo ao viewModel.

Obrigado pelas respostas.

//ctor of ViewModel 
public ViewModel()
{ 
db = new PackageShipmentDBEntities(); // Entity Framework generated class

ListaZBazy = new ObservableCollection<Pack>(db.Packs.Where(w => w.IsSent == false)); 
}

Este é o meu ctor habitual do ViewModel, acho que existe uma maneira melhor, eu estava lendo sobre o padrão do repositório, não tenho certeza se posso adaptá-lo ao WPF MVVM

hal9k2
fonte

Respostas:

4

Analisei bastante esse assunto e não encontrei uma solução "perfeita". O padrão de repositório funciona maravilhosamente para aplicativos MVC em que o contexto é de curta duração porque existe em um controlador de curta duração, mas o problema ocorre quando você tenta aplicar a mesma estrutura a um aplicativo wpf em que a VM pode persistir por longos períodos de tempo.

Eu usei essa solução no passado, que é muito mais simples do que muitos dos padrões de repo que vi que tentam abstrair as coisas a uma quantidade extrema, resultando em quantidades quase ilegíveis de código que são difíceis de depurar. Aqui estão os passos ...

  1. Crie um projeto separado para o EDMX atuar como sua camada de acesso a dados
  2. Crie uma pasta "Repositórios" no mesmo projeto
  3. Crie uma classe base "BaseRepository" para atuar como a "Unidade de Trabalho". IDisposablepermitirá que você use isso em um using(){}e partialpermitirá implementar outros repositórios

    public partial class MyEntityRepository : IDisposable
    {
        MyEntities context = new MyEntities();
    
        public void Dispose()
        {
            context.Dispose();
        }
    }
  4. Crie outro arquivo chamado "MyOtherRepository". crie a mesma classe parcial, mas implemente métodos com base no que você deseja que esse arquivo contenha

    public partial class MyEntityRepository
    {
        public void MyOtherMethodSave(EntityObject obj)
        {
            //work with context
            ...
    
            context.SaveChanges();
        }
    }

Agora, na sua VM, você pode fazer isso ...

using(MyEntityRepository repo = new MyEntityRepository())
{
     repo.MyOtherMethodSave(objectToSave);
}

Isso agrupa todos os seus repositórios em uma classe para que você não precise lidar com um contexto separado. Ele permite que você gerencie melhor os diferentes repositórios, agrupando os métodos em arquivos diferentes e ajuda a impedir a duplicação de código. Além disso, seus contextos têm uma vida útil curta, sem usar esse padrão.

A desvantagem é que, em sistemas maiores, você pode ter muitos métodos agrupados no seu repositório. Uma solução nesse caso seria implementar alguns comandos comuns básicos, como "Localizar" ou "Adicionar", e implementar comandos especializados em seus respectivos repositórios.

Sapato
fonte
2
Você pode substituir o próprio contexto do EF 'MyEntityRepository' e obter o mesmo resultado. A menos que você deseje agrupar o contexto da EF com seu próprio "repositório", aumentando a duplicação.
Euphoric
@Euphoric Sim, você poderia, mas não terá a garantia de que o repositório seja usado no contexto. A questão toda é abstrair a forma como EF obras em requisitos de negócio simples
Shoe
4

Oposto a repositórios, dos quais não gosto. Eu recomendaria usar o padrão de comando, conforme recomendado por Ayende .

Simplesmente disse, para cada operação, você cria uma ThisOperationCommandclasse separada . Dentro desta classe, você trabalhará com o contexto EF normal. Você pode até usar alguma classe base EFCommandque faça algum encanamento para você.

Do lado do ViewModel, você cria uma instância desse comando, preenche-o com parâmetros (você pode até passar a instância inteira do ViewModel se não se importar com o acoplamento rígido entre o comando e o ViewModel) e depois passá-lo para algum tipo de Executemétodo, que será iniciado faça o comando, execute-o, derrube-o e depois retorne o que o comando recebeu. Você também pode retornar vários valores se obtê-lo da instância do comando após a execução.

A vantagem é que você não precisa duplicar toda a camada de acesso a dados como repositório e pode reutilizar e compor comandos, desde que crie alguma infraestrutura simples para suportá-la. Por exemplo, executando comandos de outros comandos.

Eufórico
fonte
0

Para cenários simples, usei o seguinte:

public class ViewModel : IDisposable {

    private EntitiesContext _context = new EntitiesContext();

    private SomeEntity _model;
    public SomeEntity Model {
       get { return _model; }
    }

    public View(int id) {
        _model = _context.SomeEntity.Find(id);
    }

    private ICommand _saveCommand = new RelayCommand(() => _context.SaveChanges());
    public ICommand SaveCommand {
        get { return _saveCommand; }
    }        

    public void Dispose() {
         _context.Dispose();
    }

}
Mike
fonte
11
O problema com isso é que seu contexto agora é potencialmente "vida longa".
Shoe
11
Você não deve criar a instância do contexto dentro da classe, mas injetando-a no construtor.
Oscar Mederos