Reputation: 2427
I'm relatively new to MVVM and I'm wondering about the best way to structure my application. Here is a sample of my models:
public class ModelSource : ModelBase
{
#region Fields
private int _isLoading;
private BackgroundWorker worker = new BackgroundWorker();
private ObservableCollection<PCDatabase> _databases;
#endregion //Fields
#region Properties
public ObservableCollection<PCDatabase>Databases
{
get
{
if (_databases == null)
{
_databases = new ObservableCollection<PCDatabase>();
}
return _databases;
}
set
{
_databases = value;
this.OnPropertyChanged("Databases");
}
}
public int IsLoading
{
get
{
return _isLoading;
}
set
{
_isLoading = value;
OnPropertyChanged("IsLoading");
}
}
#endregion
#region Methods
/// <summary>
/// Gets all Databases from the Server
/// </summary>
public void getDatabasesAsync(ConfigDatabaseConnection _currentConfig)
{
//execute SQL Query...
}
(ModelBase implements INotifyPropertyChanged).
Here is my corresponding ViewModel:
namespace DbRestore.ViewModel
{
public class ViewModelSource : ViewModelBase
{
private ObservableCollection<PCDatabase> _databases;
private ModelSource _modelSource;
private ICommand _populateDatabaseCommand;
public ConfigDatabaseConnection _currentConfig;
public ViewModelSource()
{
this.ModelSource = new ModelSource();
}
#region Commands
/// <summary>
/// Command that opens a Database Connection Dialog
/// </summary>
public ICommand OpenDataBaseConnectionCommand
{
get
{
if (_populateDatabaseCommand == null)
{
_populateDatabaseCommand = new RelayCommand(
param => this.PopulateDatabases()
);
}
return _populateDatabaseCommand;
}
}
public ObservableCollection<PCDatabase> Databases
{
get
{
return _databases;
}
set
{
_databases = value;
OnPropertyChanged("Databases");
}
}
#endregion //Commands
public void PopulateDatabases()
{
ModelSource.getDatabasesAsync(_currentConfig);
}
Calling ModelSource.getDatabasesAsync(_currentConfig) gets my SQL Data in my model. Due to some of my SQL queries being quite complex, I've implemented a Background Worker that runs these queries asynchronously.
How do I get the data into my ViewModel, which is bound to my View? Or is my design approach as a whole faulty?
Things I've considered and tried:
Binding directly to the model: Works, but I've been told that this is a
bad practice, and the application logic should reside in the Model.
Moving the SQL queries into the ViewModel: Also works, but then my Model class seems to be redundant - it would be nothing but a custom datatype.
Run the queries synchronously and directly assign the Observable Collection in my model to the Observable Collection in my ViewModel. Also works, but then I'm running into problems with my BackgroundWorker, because the ViewModel won't know when the Query is actually finished.
Upvotes: 1
Views: 511
Reputation: 6882
Have you seen these articles?
Async Programming : Patterns for Asynchronous MVVM Applications: Data Binding https://msdn.microsoft.com/en-us/magazine/dn605875.aspx
Async Programming : Patterns for Asynchronous MVVM Applications: Commands https://msdn.microsoft.com/en-us/magazine/dn630647.aspx
These should cover a good strategy especially when working with async/await.
Upvotes: 1
Reputation: 1578
PCDatabase
is OK.Since you're using BackgroundWorker
, I assume you use .NET Framework 3.5 and don't have TPL.
public interface IPCDatabaseRepository
{
void GetPCDatabasesAsync(Action<IList<PCDatabase>> resultHandler);
}
public class PCDatabaseRepository : IPCDatabaseRepository
{
public void GetPCDatabasesAsync(Action<IList<PCDatabase>> resultHandler)
{
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) =>
{
args.Result = // Execute SQL query...
};
worker.RunWorkerCompleted += (sender, args) =>
{
resultHandler(args.Result as IList<PCDatabase>);
worker.Dispose();
};
worker.RunWorkerAsync();
}
}
public class ViewModelSource : ViewModelBase
{
private readonly IPCDatabaseRepository _databaseRepository;
private ObservableCollection<PCDatabase> _databases;
private bool _isBusy;
public ViewModelSource(IPCDatabaseRepository databaseRepository /*Dependency injection goes here*/)
{
_databaseRepository = databaseRepository;
LoadDatabasesCommand = new RelayCommand(LoadDatabases, () => !IsBusy);
}
public ICommand LoadDatabasesCommand { get; private set; }
public ObservableCollection<PCDatabase> Databases
{
get { return _databases; }
set { _databases = value; OnPropertyChanged("Databases"); }
}
public bool IsBusy
{
get { return _isBusy; }
set { _isBusy = value; OnPropertyChanged("IsBusy"); CommandManager.InvalidateRequerySuggested(); }
}
public void LoadDatabases()
{
IsBusy = true;
_databaseRepository.GetPCDatabasesAsync(results =>
{
Databases = new ObservableCollection(results);
IsBusy = false;
});
}
Upvotes: 2