2015-06-19 24 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>>>。如果這種懶惰的異步操作暴露在外面,它可能會混淆,因爲它會阻塞。如果這是一個內部單呼的情況下,我會去的: