I am currently rapid prototyping a WPF application following the MVVM pattern and using the Entity Framework and SQL Servre CE.
I am experimenting with the AsyncCommand by Stephen Cleary from the MSDN blog.
Everything is working well with the exception of Context.SaveChangesAsync()
which is still blocking the UI.
This is the synchronous code which takes about 7 seconds for 10000 "Entities" (a very simple data type):
public int Insert( IEnumerable<Entity> entities )
{
using( Context context = new Context( ConnectionString ) )
{
context.Entities.AddRange( entities );
return context.SaveChanges();
}
}
This is the asynchronous version which is still blocking the UI (I also tried a version without the using statement but that did not help):
public async Task<int> InsertAsync( IEnumerable<Entity> entities )
{
using( Context context = new Context( ConnectionString ) )
{
context.Entities.AddRange( entities );
return await context.SaveChangesAsync();
}
}
The bulk of the work happens in SaveChangesAsync()
and wrapping AddRange does not help
await Task.Run( () => context.Entities.AddRange(entities));
Using context.SaveChangesAsync().ConfigureAwait(false)
does not help either.
However, if I simply wrap the synchronous version then it is NOT blocking:
public async Task<int> InsertAsync( IEnumerable<Entity> entities )
{
return await Task<int>.Run( () => Insert( entities ) );
}
This code is called from the ViewModel using an AsyncCommand
(see link above for the MSDN article):
CreateRandomEntitiesAsyncCommand = AsyncCommand.Create(
token => CreateRandomEntitiesAsync(token));
public IAsyncCommand CreateRandomPatientsAsyncCommand { get; private set; }
public async Task CreateRandomAsync()
{
int numberToGenerate = 10000;
// Get all existing unique ids from the data layer, this does not block
IEnumerable<string> existingUniqueIDs = await dataLayer.GetUniqueIDsNoTrackingAsync();
// randomly create 10000 entities, ensuring they are unique, does not block either
Entity[] entities = await Task.Run( () =>
CreateEntities(numberToGenerate, existingUniqueIDs));
// this does block, see above
await dataLayer.InsertAsync( entities );
// ...
// Re-fetch all entities from the db and issue PropertyChanged
// for the Entities property to refresh the UI.
// The code above is still blocking if this is commented out.
}
What am I making wrong?
Intermediate update
It seems this issue is related to SQL Server CE and its "underlying implementation" possibly mapping the asynchronous Entity Framework code to synchronous code so that it is executed on the UI thread and thus blocking the UI.
Also see: Blocking behaviour with Entity Framework Async methods and SQL Server Compact
I am using (via NuGet):
- EntityFramework.SqlServerCompact v6.1.3 by Microsoft
- Microsoft.SqlServerCompact v4.0.8876.1 [Is this the ADO.Net provider?]