1. O mesmo Repository, chamado de um lugar diferente
Não existe nada de especial em IClienteRepository quando usado numa
aplicação desktop — é a mesma interface, a mesma implementação (ADO.NET, Dapper ou EF Core, Módulo
1 capítulos 13/15) usada em MVC e API. O que muda é quem chama: em vez de um
Controller reagindo a uma requisição, é um ViewModel reagindo a um Command.
2. Nunca bloqueie a thread de UI
Toda aplicação com interface gráfica tem uma thread de UI dedicada a desenhar a
tela e responder a cliques. Se essa thread ficar ocupada esperando uma chamada ao banco terminar,
a janela inteira "congela" — não redesenha, não responde a cliques, o Windows chega a marcar como
"Não está respondendo". async/await (Módulo 1, capítulo 10) existe
exatamente para isso.
private void SalvarCommand_Execute(object? parametro)
{
_repositorio.InserirAsync(cliente).Wait(); // BLOQUEIA a thread de UI
Mensagem = "Salvo!";
}
private async Task SalvarAsync()
{
await _repositorio.InserirAsync(cliente); // libera a thread de UI durante a espera
Mensagem = "Salvo!";
}
.Wait()/.Result numa Task
ainda em andamento é exatamente o anti-padrão descrito naquele capítulo — e numa aplicação
desktop, o efeito colateral é visível e imediato: a janela trava. É o cenário mais concreto
possível do motivo de nunca misturar código síncrono com assíncrono.
3. Indicando progresso durante a espera
Já que a operação é assíncrona (não trava a UI), vale mostrar ao usuário que algo está acontecendo — outra propriedade no ViewModel, ligada a um indicador visual:
private bool _carregando;
public bool Carregando
{
get => _carregando;
set => DefinirCampo(ref _carregando, value);
}
private async Task SalvarAsync()
{
Carregando = true;
try
{
await _repositorio.InserirAsync(new Cliente { Nome = Nome, Email = Email });
Mensagem = "Cliente salvo com sucesso!";
}
catch (Exception ex)
{
Mensagem = $"Erro: {ex.Message}";
}
finally
{
Carregando = false;
}
}
<ProgressBar IsIndeterminate="True"
Visibility="{Binding Carregando, Converter={StaticResource BoolParaVisibilidade}}" />
<Button Content="Salvar" Command="{Binding SalvarCommand}"
IsEnabled="{Binding Carregando, Converter={StaticResource InversoBoolParaBool}}" />
WPF não converte automaticamente um bool para
Visibility — um IValueConverter
customizado faz essa ponte. É um detalhe específico de XAML, mas o princípio (mostrar/ocultar com
base num estado do ViewModel) é o mesmo de qualquer spinner de carregamento que você já usou numa
SPA JavaScript.
4. Cache local — reduzindo idas ao banco
Diferente de uma API stateless, uma aplicação desktop pode manter dados de referência (categorias de produto, lista de status) em memória durante toda a sessão, evitando reconsultar o banco a cada tela:
public class CacheReferenciaService
{
private List<Categoria>? _categorias;
public async Task<List<Categoria>> ObterCategoriasAsync(ICategoriaRepository repositorio)
{
_categorias ??= await repositorio.ListarAsync();
return _categorias;
}
}
5. Onde isso te leva
Com acesso a dados assíncrono e responsivo coberto, o último capítulo deste módulo (e do curso) junta tudo numa aplicação desktop completa: MVVM, navegação, data binding e as procedures do Módulo 2, do início ao fim.