2015-06-19 26 views
3

使用我的存储库,所有实施IRespository有一个大的代码库,我实现这些方法异步版本:迁移到异步:库

T Find(id); 
Task<T> FindAsync(id); 
...etc... 

有几种类型的存储库。最简单的方法是基于一个不可变的集合,其中实体的大小足够小,可以从数据库中一次加载它们。这种负载在任何人第一次调用任何IRepository方法时发生。例如,Find(4)会在未发生负载时触发负载。

我用懒惰< T>实现了这个。非常方便,并已工作多年。

我无法在异步版上使用冷火鸡,所以我必须在同步版本旁边添加异步版。我的问题是,我不知道哪个将被首先调用 - 在存储库上的同步或异步方法。

我不知道如何申报我的懒惰 - 如果我去做了,因为我一直在做它,

Lazy<MyCollection<T>> 

然后加载时FindAsync()首次调用它不会是异步。在另一方面,如果我去

Lazy<Task<MyCollection<T>>> 

这将是伟大的FindAsync(),但如何将同步方法触发初始加载W/O有关死锁调用Task.Result触犯克利先生的警告的运行?

谢谢你的时间!

回答

2

的问题Lazy<T>是,只有一个工厂方法。如果第一个调用是同步的,你真正想要的是一个同步工厂方法,如果第一个调用是异步的,则你需要一个异步工厂方法。 Lazy<T>不会为你做到这一点,AFAIK也没有其他内置的提供这些语义的内容。

你可以,但是,自己建一个:

public sealed class SyncAsyncLazy<T> 
{ 
    private readonly object _mutex = new object(); 
    private readonly Func<T> _syncFunc; 
    private readonly Func<Task<T>> _asyncFunc; 
    private Task<T> _task; 

    public SyncAsyncLazy(Func<T> syncFunc, Func<Task<T>> asyncFunc) 
    { 
    _syncFunc = syncFunc; 
    _asyncFunc = asyncFunc; 
    } 

    public T Get() 
    { 
    return GetAsync(true).GetAwaiter().GetResult(); 
    } 

    public Task<T> GetAsync() 
    { 
    return GetAsync(false); 
    } 

    private Task<T> GetAsync(bool sync) 
    { 
    lock (_mutex) 
    { 
     if (_task == null) 
     _task = DoGetAsync(sync); 
     return _task; 
    } 
    } 

    private async Task<T> DoGetAsync(bool sync) 
    { 
    return sync ? _syncFunc() : await _asyncFunc().ConfigureAwait(false); 
    } 
} 

或者,你可以使用这个模式没有它封装:

private readonly object _mutex = new object(); 
private Task<MyCollection<T>> _collectionTask; 

private Task<MyCollection<T>> LoadCollectionAsync(bool sync) 
{ 
    lock (_mutex) 
    { 
    if (_collectionTask == null) 
     _collectionTask = DoLoadCollectionAsync(sync); 
    return _collectionTask; 
    } 
} 

private async Task<MyCollection<T>> DoLoadCollectionAsync(bool sync) 
{ 
    if (sync) 
    return LoadCollectionSynchronously(); 
    else 
    return await LoadCollectionAsynchronously(); 
} 

的“布尔同步”模式是一个斯蒂芬Toub给我看最近。 AFAIK没有博客或任何关于它的东西。

+0

我希望你能看到这个并称重 - 这真的很有帮助,谢谢!也真的很享受你的书! – n8wrl

0

Task旨意只运行一次,但你可以await他们很多次,只要你想,你也可以拨打他们Wait()Result结束后并不会阻止。

将异步方法转换为状态机,该状态机调度每个await在等待完成后运行的代码。但是,如果等待已经完成,代码将立即运行。所以,等待已完成的候选人的开销不大。

对于那些小型的内存中存储库,您可以使用Task.FromResult返回完成的Task。你可以缓存任何Task并随时等待。

0

一个同步方法如何触发初始加载没有运行克莱里先生关于调用Task.Result的死锁警告的冲突?

您可以使用同步版本并使用Task.FromResult加载您的Lazy<Task<MyCollection<T>>>。如果这种懒惰的异步操作暴露在外面,它可能会混淆,因为它会阻塞。如果这是一个内部单呼的情况下,我会去的: