微软的StorageFile只支持异步的方式进行文件操作,我之前也封装过一个StorageHelper,但是当所有的方法都是异步的时候也带来一些问题
1、比如我们不能在构造函数调用异步代码(等待),
2、比如我们在离开App的时候我们需要对数据进行快速的保存(在事件中),这个时候就不适合用异步了,异步可能会导致保存失败,因为App已经不在前台了
最近就遇到了一个这样的需求
我封装了一个SqliteHelper类,提供了一些数据库操作的一些常用方法,在数据库使用之前,需要保证数据库路径的文件夹是存在的,如果不能存在,则会抛出异常,所以在SqliteHelper的构造函数判断该文件夹是否存在,如果不存在,则创建该文件夹,但是StorageHelper不支持同步方法(这里不使用IsolatedStorage),我们需要保证在StorageHelper在构造完成时,保证文件夹存在
public SqliteHelper(string dbPath) { this.dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, dbPath); var directory = Path.GetDirectoryName(dbPath); if (directory != null) { //TODO:构造函数不支持await关键字 //await StorageHelper.Instance.CreateDirectoryAsync(directory); } }
满足上面需求可以有两个方法:
1、通过一个静态异步方法来构造SqliteHelper的实例,把异步操作放到异步方法里面,但是这种方法会导致不能使用依赖注入IoC
private SqliteHelper(string dbPath) { this.dbPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, dbPath); } public static async Task<SqliteHelper> GetSqliteHelper(string dbPath) { var sqliteHelper = new SqliteHelper(dbPath); var directory = Path.GetDirectoryName(dbPath); if (directory != null) { await StorageHelper.Instance.CreateDirectoryAsync(directory); } return sqliteHelper; }
2、让异步方法同步执行
我们首先定义一个异步执行Helper,提供异步方法的同步(摘自后面的参考文章)
using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace TestDemo { public static class AsyncInline { public static void Run(Func<Task> item) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); synch.Post(async _ => { try { await item(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); } public static T Run<T>(Func<Task<T>> item) { var oldContext = SynchronizationContext.Current; var synch = new ExclusiveSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(synch); T ret = default(T); synch.Post(async _ => { try { ret = await item(); } catch (Exception e) { synch.InnerException = e; throw; } finally { synch.EndMessageLoop(); } }, null); synch.BeginMessageLoop(); SynchronizationContext.SetSynchronizationContext(oldContext); return ret; } private class ExclusiveSynchronizationContext : SynchronizationContext { private bool done; public Exception InnerException { get; set; } readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false); readonly Queue<Tuple<SendOrPostCallback, object>> items = new Queue<Tuple<SendOrPostCallback, object>>(); public override void Send(SendOrPostCallback d, object state) { throw new NotSupportedException("We cannot send to our same thread"); } public override void Post(SendOrPostCallback d, object state) { lock (items) { items.Enqueue(Tuple.Create(d, state)); } workItemsWaiting.Set(); } public void EndMessageLoop() { Post(_ => done = true, null); } public void BeginMessageLoop() { while (!done) { Tuple<SendOrPostCallback, object> task = null; lock (items) { if (items.Count > 0) { task = items.Dequeue(); } } if (task != null) { task.Item1(task.Item2); if (InnerException != null) // the method threw an exeption { throw new AggregateException("AsyncInline.Run method threw an exception.", InnerException); } } else { workItemsWaiting.WaitOne(); } } } public override SynchronizationContext CreateCopy() { return this; } } } }
接下来我们就可以像同步方法一样使用异步方法了
public class TestClass { public int Id { get; set; } public TestClass() { //这里调用一个异步方法,等待异步方法执行返回 Id = AsyncInline.Run(GetIdAsync); //不支持await //await GetIdAsync(); } /// <summary> /// 异步方法 /// </summary> public async Task<int> GetIdAsync() { //TODO:这里执行模拟自定义操作 await Task.Delay(1000); return 10; } }
参考文章:
http://stackoverflow.com/questions/8145479/can-constructors-be-async
个人能力有限,如果有更好的实现,可以给我留言
转载请注明出处:http://www.cnblogs.com/bomo/p/3945761.html