2012-01-05 152 views
4

我想聽聽有關處理與Command模式異步操作的最佳方式的意見。假設我們有下面的例子:在C#中處理異步操作的命令模式和異步操作

public class MyCommand 
{ 
    // Sets up receiver and does whatever stuff 

    public void Execute() 
    { 
     _myReceiver.DoSomething(); 
    } 
} 

的問題是:mycommand的不知道MyReceiver.DoSomething()是否有代碼的異步部分。如果我想在執行後將MyCommand推入撤消堆棧,我無法保證其接收器操作已完全執行,因此無法確定MyCommand是否達到撤銷可能或不可撤銷的狀態。

我個人認爲以下解決方案:

  1. 實現某種在命令國家控制的
  2. 在命令中包含「BeginExecute」和「EndExecute」
  3. 納入MyReceiver事件和make命令訂閱他們(似乎臭我)

爲了總結的東西了,mycommand的會變成:

public class MyCommand 
{ 
    public MyCommand(MyReceiver receiver) 
    { 
     _myReceiver = receiver; 
     _myReceiver.DoSomethingFinished +=() => this.EndExecute(); 
    } 

    public void BeginExecute() 
    { 
     this.EnterExecutionState(); 

     _myReceiver.DoSomething(); 
    } 

    public void EndExecute() 
    { 
     this.LeaveExecutionState(); 
    } 

    // State handling related stuff 
} 

我現在有辦法確保Command的接收器已經完成執行任何動作,並且它已準備好被推入撤消堆棧。但是,對於事件垃圾郵件,每個包含異步操作的Receiver類都會讓我感到不安。

我在互聯網上沒有發現太多有關這個主題的內容,並且很想聽聽不同的方法。

OBS:使命令管理所有與異步相關的代碼不是一個選項:)。

+0

你怎麼能說你不知道是否_myReceiver.DoSomething();有一些異步屬性。它要麼完成,所以你知道它已經完成了,要麼它會返回一些讓你控制異步行爲的東西。無論哪種方式,_myReceiver.DoSomething()都可能有問題。而不是你的命令。 – Euphoric 2012-01-05 21:25:59

+0

Async除了撤消部分看起來如何 - 命令一個UndoExecution或...? – 2012-01-05 21:27:20

+0

@Euphoric的DoSomething方法可以.BeginInvoke的東西... – 2012-01-05 21:29:20

回答

1

像這樣的事情?

public interface ICommand 
{ 
    void Execute(); 
    event EventHandler Finished; 
} 

public class MyCommand : ICommand 
{ 
    public MyCommand(MyReceiver receiver) 
    { 
     _myReceiver = receiver; 
     _myReceiver.DoSomethingFinished +=() => Finished(); // dont forget null check here. 
    } 

    public void Execute() 
    {  
     _myReceiver.DoSomething(); 
    } 

    public event EventHandler Finished; 
} 

這樣一來,該命令的用戶可以註冊,所以它知道當命令已經完成它的異步行爲,可以acordingly採取行動,完成事件。

或者如果你不想使用事件,那麼回調呢?

public class MyCommand : ICommand 
{ 
    public MyCommand(MyReceiver receiver) 
    { 
     _myReceiver = receiver; 
    } 

    public void Execute() 
    {  
     _myReceiver.DoSomething(() => Finished()); // dont forget null check here. 
    } 

    public event EventHandler Finished; 
} 

無論採用哪種方式,只需要有一種方式讓MyReciever通知其調用者它已完成。沒有辦法繞過它。

+0

謝謝您的回答,但問題與指示命令完成無關。爲了簡單起見,我實際上忽略了這部分。我希望看到在Receiver中使用事件是不必要的方法。在你的例子中,我仍然不得不通過事件來處理每個異步操作的垃圾郵件。 – ferspanghero 2012-01-05 21:42:23

+0

@ferspan:除非在MyReciever和其他所有方面都有共同的抽象,否則將以相同的方式使用,那麼我沒有看到可能的解決方案。 這是當前編程中非常大的問題,並且全新的C#版本會針對異步,等待和任務提出此問題。 – Euphoric 2012-01-05 21:49:38

+0

謝謝!你的方法最終與Tigram相似。因此,我問你同樣的問題:考慮_myReceiver.DoSomething(Action action)會產生很多異步調用,我如何確保在所有異步調用完成後執行回調? – ferspanghero 2012-01-05 22:04:59

1

首先,我會添加方法Async的名稱,以明確地向您的Command類消費者發信號通知該方法以異步方式執行。

其次,我會添加像參數一個Action<T>這將被稱爲方法異步調用完成。因此,當異步結束時,可以通知此方法調用方。

編輯

obj.DoSomethingAsync(... params, Action<T> onComplete)

+0

在哪裏我會添加一個動作參數?如果它在BeginExecute中,這將是沒有用的,因爲_myReceiver.DoSomething()會以相同的方式返回,這使我們無法瞭解它的異步狀態。如果它在_myReveiver.DoSomething()中,我仍然堅持事件解決方案,我發現它更乾淨。 – ferspanghero 2012-01-05 21:36:42

+0

@ferspan:如果我在那個類中有20個異步方法,並且在其中執行幾個異步方法會怎麼樣。你將如何處理這個問題?每種方法都必須定義*它自己的事件?或者事件會帶來一個方法定義? **伊莫**,'行動'模式更清潔 – Tigran 2012-01-05 21:45:55

+0

@ferspan:看到我編輯過的帖子。 – Tigran 2012-01-05 21:57:28

1

如果要強制要求在控制返回到Execute方法之前完成所有處理,而不修改調用代碼的行爲,則可以修改操作的執行方式。

首先初始化所有異步調用,並在當前線程上阻塞(等待)以返回調用。我不確定異步調用的本質是什麼,就像它們在您知道的線程中,或者將在任意線程中返回一樣,但您應該能夠創建某種線程同步您的問題。

嘗試使用Semaphore來阻止當前線程(在調用異步方法之後),並在所有異步方法返回響應時釋放信號量。這將會產生「重新同步」異步調用的效果。

您可以使用另一種同步方法,但信號量很容易理解。

2

我覺得你在單個課堂上的表現太多了。我想打破它,就像這樣:

// An immutable command, to be handled in-process. 
// ICommand is a marker interface with no members. 
public class DoSomething : ICommand 
{ 
    public readonly Id; 

    public DoSomething(Guid id) 
    { 
     Id = id; 
    } 
} 

// To be handled out-of-process. 
[AsynchronousCommand] 
public class DoSomethingThatTakesAReallyLongTime : ICommand 
{ 
    public readonly Id; 

    public DoSomethingThatTakesAReallyLongTime(Guid id) 
    { 
     Id = id; 
    } 
} 

// This guy could take any number of dependencies: ISomethingRepository, DbContext, etc. 
// Doesn't matter, but it's probably gonna have dependencies. 
public class DoSomethingHandler : IHandler<DoSomething> 
{ 
    public void Handle(DoSomething command) // IHandler<T>'s only member 
    { 
     // CRUD or call call a domain method 
    } 
} 

public class CommandService : ICommandService 
{ 
    public void Execute(params ICommand[] commands) // ICommandService's only member 
    { 
     foreach(var command in commands) 
     { 
      var handler = GetHandler(command); // Could use your IOC container. 

      if (HasAsyncAttribute()) 
       new Action(() => handler.Handle(command)).BeginInvoke(null, null); 
      else 
       handler.Handle(command); 
     } 
    } 
} 

// Something that might consume these 
public class SomethingController 
{ 
    private readonly ICommandService _commandService; 

    public SomethingController(ICommandService commandService) 
    { 
     _commandService = commandService; 
    } 

    [HttpPost] 
    public void DoSomething(Guid id) 
    { 
     _commandService.Execute(new DoSomething(id)); 
    } 

    [HttpPost] 
    public void DoSomethingThatTakesAReallyLongTime(Guid id) 
    { 
     _commandService.Execute(new DoSomethingThatTakesAReallyLongTime(id)); 
    } 
} 

這裏最大的好處是,你可以不沿着所有與處理程序走的依賴性明確拖動分發您的命令給客戶。客戶不應該知道處理程序。所有客戶端需要知道的是它發送了一個命令,並且所有的命令都應該被假定爲成功。

+0

非常好。謝謝你!裝飾者的勝利! – trailmax 2013-11-23 00:17:35