我目前正在按照MVVM模式并使用实体框架和SQL Servre CE快速构建WPF应用程序原型。
我正在试验AsyncCommand by Stephen Cleary from the MSDN blog。
除了仍然阻止用户界面的Context.SaveChangesAsync()
之外,一切都运行良好。
这是同步代码,10000"实体"需要约7秒。 (一种非常简单的数据类型):
public int Insert( IEnumerable<Entity> entities )
{
using( Context context = new Context( ConnectionString ) )
{
context.Entities.AddRange( entities );
return context.SaveChanges();
}
}
这是仍在阻止UI的异步版本(我也试过没有using语句的版本,但没有帮助):
public async Task<int> InsertAsync( IEnumerable<Entity> entities )
{
using( Context context = new Context( ConnectionString ) )
{
context.Entities.AddRange( entities );
return await context.SaveChangesAsync();
}
}
大部分工作都在SaveChangesAsync()
进行,并且包装AddRange无效
await Task.Run( () => context.Entities.AddRange(entities));
使用context.SaveChangesAsync().ConfigureAwait(false)
也无济于事。
但是,如果我只是包装同步版本,那么它就不会阻塞:
public async Task<int> InsertAsync( IEnumerable<Entity> entities )
{
return await Task<int>.Run( () => Insert( entities ) );
}
使用AsyncCommand
从ViewModel调用此代码(请参阅上面的MSDN文章链接):
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.
}
我做错了什么?
中级更新
这个问题似乎与SQL Server CE及其基础实现有关。可能将异步实体框架代码映射到同步代码,以便在UI线程上执行,从而阻止UI。
另见:Blocking behaviour with Entity Framework Async methods and SQL Server Compact
我正在使用(通过NuGet):