2016-10-16 35 views
9

我設計一個流暢的API和使用情況處置對象是有點像這樣:從同一個對象

IUser user = work 
       .Timeout(TimeSpan.FromSeconds(5)) 
       .WithRepository(c => c.Users) 
       .Do(r => r.LoadByUsername("matt")) 
       .Execute(); 

所以,讓我們說,workIUnitOfWork型的,而是叫IActionFlow<IUserRepository>WithRepository(c => c.Users)返回一個接口中的方法這是IDisposable

當我打電話給Execute()並得到最終結果時,我失去了對該IActionFlow<IUserRepository>實例的引用,所以我無法處理它。

使實例在Execute()方法中自行展開有什麼缺點?

喜歡的東西:

public TResult Execute() 
{ 
    // ... 
    Dispose(); 
    return result; 
} 

的代碼看起來編譯得很好,但我在尋找怪異的行爲或錯誤可能上升,因爲這一點。是不是很差勁?

+0

嗯,它只是意味着你返回一個處理對象,這看起來很奇怪:)。流暢接口有一些問題:https://ocramius.github.io/blog/fluent-interfaces-are-evil/有一個完整的討論在這裏使用流暢的接口來配置模式:https:// davefancher。com/2015/06/14/functional-c-fluent-interfaces-and-functional-method-chaining/ –

回答

5

您可以Using方法是這樣的:

public static TResult Using<TDisposable, TResult>(Func<TDisposable> factory, 
    Func<TDisposable, TResult> fn) where TDisposable : IDisposable { 
    using (var disposable = factory()) { 
     return fn(disposable); 
    } 
} 

那麼你的代碼應該是這樣的:

var user = Using(() => work. 
    Timeout(TimeSpan.FromSeconds(5)). 
    WithRepository(c => c.Users), 
    repository => repository.Do(r => r.LoadByUsername("matt")). 
     Execute()); 

這將使你的API保持流暢,並在同一時間,你將處理WithRepository同一時刻Execute完成。

+0

我喜歡解決方案的聰明之處,但它是過度殺傷。 –

3

選項1:

你可以包裝using塊的內部代碼,以便處置將自動被調用,

using(var repository = work.Timeout(TimeSpan.FromSeconds(5)).WithRepository(c => c.Users)) 
{ 
    IUser user = repository 
       .Do(r => r.LoadByUsername("matt")) 
       .Execute(); 

} 

因此,您Execute方法不需要調用Dispose()

public TResult Execute() 
{ 
    // ... 
    //Dispose(); 
    return result; 
} 

選項2:

您可以在屬性中分配結果並返回存儲庫對象,您可以使用該對象明確調用Dispose方法。

像這樣的東西(可以進一步重構),

using(var repository = work 
       .Timeout(TimeSpan.FromSeconds(5)) 
       .WithRepository(c => c.Users) 
       .Do(r => r.LoadByUsername("matt")) 
       .Execute()) 
{ 

    IUser user = repository.Result; 
    //repository.Dispose(); 
} 

//****** 

public TResult Result { get; set; } 

public IActionFlow<IUserRepository> Execute() 
{ 
    // ... 
    //Dispose(); 
    this.Result = result; 
    return this; 
} 

注:調用Dispose()方法,垃圾收集後,可以在基於現有的服務器資源的任何階段發生。因此,在Execute方法中調用此方法可能會產生意想不到的奇怪問題。

+0

我可以,但這會使我的API不流利 –

+0

@MatiasCicero提供了另一種想法,但必須重構它進一步。 – Aruna

+0

甚至可以將選項2封裝在'using'塊 – Aruna

0

一次性對象必須從客戶端代碼中丟棄,您從中看到的副作用之一是,當您的客戶使用您的API時,他們會得到代碼分析錯誤(如果他們使用它, ),例如:「CA2213:應處理一次性字段」,有關該錯誤的詳細信息,請參見https://msdn.microsoft.com/en-us/library/ms182328.aspx

說了上面的,你不應該被處置的東西,是絕對必要的「執行」方法進行處理,把這些場景:

方案1

  1. 你有像您必須處理的打開連接之類的事情, 連接在除執行之外的其他方法中打開。
  2. 在「執行」方法執行期間發生錯誤,處理永遠不會被調用,因爲執行永遠不會被調用,並且打開的連接保持打開狀態。請注意,即使您的計算機永遠不會失敗,也不會因爲從其他線程發生隨機事件而導致生產失敗。

P.S.使用語句和整個IDisposable功能是爲了解決這種情況而建立的,無論情況如何,都會迫使好的開發人員進行處理。

方案2

  1. 的執行方法做了一次性值得例如打開一個連接,然後它做的東西。

    public void Execute(object whatever){ 
        using (var conn = new Connection()){ 
         //method body 
        } 
    } 
    

    如果你有場景#:

在這種情況下,你不需要Dispose方法,你可以簡單地通過包裝就像是使用語句的連接處理的執行方法的連接1,你必須讓客戶使用using語句,並且必須承擔它的非流利性(析構函數不會削減它),如果你有場景#2,你根本不需要處理。

0

它看起來像我試圖處置庫。

如果你想保持你的API完好,我建議這樣的:

using(var repo = work.Timeout(TimeSpan.FromSeconds(5)) 
     .WithRepository(c => c.Users)) 
{ 
    IUser user = repo.Do(r => r.LoadByUsername("matt")).Execute(); 
    //Do whatever with user here if lazy loading. 
} 

我知道這種斷裂的兩個你流利的API,但你可以用這種方式沒有根本改變任何使用它。

另一種選擇是創建一個包裝類爲維護一個參考庫(如ActionFlowExecution),並實現了IDisposable結果:

public class ActionFlowExecution<TResult, TRepository> : IDisposable 
{ 
    private TRepository _repository; 
    internal ActionFlowExecution(TResult result, TRepository repository) 
    { 
     Result = result; 
     _repository = repository; 
    } 

    public TResult Result { get; private set; } 

    public void Dispose() 
    { 
     if(_repository != null) 
     { 
      _repository.Dispose(); 
      _repository = null; 
     } 
    } 
} 

然後,您可以打電話給你的API是這樣的:

using(var execution = work 
      .Timeout(TimeSpan.FromSeconds(5)) 
      .WithRepository(c => c.Users) 
      .Do(r => r.LoadByUsername("matt")) 
      .Execute()) 
{ 
    IUser user = execution.Result; 
    //Do whatever with user here 
} 

我同意其他人的觀點,認爲從內部處理對象是一個壞主意。 我在五分鐘內寫完了,所以可能會出現一些拼寫錯誤,但我認爲你會得到一般想法。

0

只要稍後不再使用該對象,就可以安全地進行所提及的操作。這裏唯一但非常重要的是命名。我會打電話給方法ExecuteAndDispose。其他選擇或者打破流利的API,或者只是矯枉過正,也不容易使用。