Problem
I have a WPF application, in which I’d like to bind a collection to a combobox using an asynchronous method :
<ComboBox Margin="2,0,5,0" Width="178" ItemsSource="{Binding Animateur}" DisplayMemberPath="nom" SelectedIndex="0" />
the viewmodel class
_service.GetAnimateur((item, error) =>
{
if (error != null)
{
// TODO : traitement d'erreur
}
else
{
_Animateur.Clear();
item.ForEach(Elem =>
{
_Animateur.Add(Elem);
});
}
});
the Asynchrounous method :
public async void GetAnimateur(Action<List<fiche>, Exception> callback)
{
try
{
Task<List<fiche>> data = (Task<List<fiche>>)Task.Run(
() =>
{
DataEntities _db = new DataEntities();
var dpcs = _db.fiche;
return new List<fiche>(dpcs);
});
var result = await data;
callback(result, null);
}
catch (Exception ex)
{
callback(null, ex);
}
}
I have 20128 items in the table fiche
, the problem is that GetAnimateur
takes a lot of time to fill the combobox.
- What are the errors that I commited in this code?
- How can I improve it?
Solution
First, I want to point out the behavior of method that is declared as async void
. When the code reaches this line:
var result = await data;
The code following after _service.GetAnimateur
is executed. async void
is there only for event handlers. So using this signature creates more problems than it solves.
Second, I may not see the whole picture, but your GetAnimateur
looks weird. I think it is because the problem I’ve mentioned earlier. The most readable and understandable may be code like:
public Task<IEnumerable<fiche>> GetAnimateur()
{
return Task.Factory.StartNew(() =>
{
DataEntities _db = new DataEntities();
return (IEnumerable<fiche>) _db.fiche;
});
}
and consume like:
try
{
var list = await GetAmateur();
// success
}
catch (Exception ex)
{
// fail
}
or even:
GetAmateur().ContinueWith(t =>
{
if (t.IsCompleted)
{
var list = t.Result;
// success
}
else
{
var ex = t.Exception;
// fail
}
}, TaskScheduler.FromCurrentSynchronizationContext());
Third, when you add or remove an item from ObservableCollection
, the UI gets updated, slowing down performance. One common way to create an observable collection that supports painless multi-adding is
public class RangeObservableCollection<T> : ObservableCollection<T>
{
private bool _suppressNotification = false;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
public void AddRange(IEnumerable<T> list)
{
if (list == null)
throw new ArgumentNullException("list");
_suppressNotification = true;
foreach (T item in list)
{
Add(item);
}
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
So you simply use RangeObservableCollection
instead of ObservableCollection
.
Fourth, just don’t forget about tuning your EF properly for better performance.