Created
November 28, 2011 22:59
-
-
Save phatboyg/1402493 to your computer and use it in GitHub Desktop.
Example of applying behavior to an actor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Cleaner_version_of_defining_actor_behavior | |
{ | |
public void Some_method_in_your_code() | |
{ | |
// create the actor instance, passing the state and an initializer | |
// in this case, applying the initial behavior of to the actor | |
ActorRef agent = Actor.New<MyState>(x => x.Apply<DefaultBehavior>()); | |
// now messages can be sent to the actor to make it do things | |
// we will use a stateless actor for that interaction | |
StatelessActor.New(actor => | |
{ | |
agent.Request(new A(), actor) | |
.ReceiveResponse<B>(response => { }); | |
}); | |
} | |
// the state of an actor can be any type, and is accessible by the | |
// behaviors applied to the actor | |
class MyState | |
{ | |
public string SomeValue { get; set; } | |
} | |
class DefaultBehavior : | |
Behavior<MyState> | |
{ | |
readonly Actor<MyState> _actor; | |
// the actor is passed to the behavior, which gives it access | |
// to state. A new instance of the behavior class is created when | |
// behavior is applies, and this executes within the actor execution | |
// context | |
public DefaultBehavior(Actor<MyState> actor) | |
{ | |
_actor = actor; | |
} | |
// by a PublicMessageMethodConvention, this methods gets wired up | |
// to the inbox to deliver messages of type A as part of the behavior | |
public void Handle(Message<A> message) | |
{ | |
string lastValue = _actor.State.SomeValue; | |
_actor.State.SomeValue = message.Body.Value; | |
message.Respond(new B | |
{ | |
LastValue = lastValue | |
}); | |
// after this message, we want to keep this behavior current so we | |
// can handle more messages of type A | |
_actor.ReapplyBehavior(); | |
} | |
// receiving a disable message will change the behavior of the actor | |
public void Handle(Message<Disable> message) | |
{ | |
_actor.Apply<DisabledBehavior>(); | |
} | |
} | |
// another behavior defined for the actor | |
class DisabledBehavior : | |
Behavior<MyState> | |
{ | |
readonly Actor<MyState> _actor; | |
public DisabledBehavior(Actor<MyState> actor) | |
{ | |
_actor = actor; | |
} | |
// in this case, handling the enable message changes back to the default | |
// behavior | |
public void Handle(Message<Enable> message) | |
{ | |
_actor.Apply<DefaultBehavior>(); | |
} | |
} | |
class A | |
{ | |
public string Value { get; set; } | |
} | |
class B | |
{ | |
public string LastValue { get; set; } | |
} | |
class Disable | |
{ | |
} | |
class Enable | |
{ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Cleaner_version_of_defining_actor_behavior | |
{ | |
public void Some_method_in_your_code() | |
{ | |
// create the actor instance, passing the state and an initializer | |
// in this case, applying the initial behavior of to the actor | |
ActorRef agent = Actor.New<AgentState>(x => x.Apply<DefaultAgentBehavior>()); | |
// now messages can be sent to the actor to make it do things | |
// we will use a stateless actor for that interaction | |
StatelessActor.New(actor => | |
{ | |
agent.Request(new RequestStockQuote {Symbol = "AAPL"}, actor) | |
.ReceiveResponse<StockQuote>(response => { }); | |
}); | |
} | |
// the state of an actor can be any type, and is accessible by the | |
// behaviors applied to the actor | |
class DefaultAgentBehavior : | |
Behavior<AgentState> | |
{ | |
readonly Actor<AgentState> _actor; | |
// the actor is passed to the behavior, which gives it access | |
// to state. A new instance of the behavior class is created when | |
// behavior is applies, and this executes within the actor execution | |
// context | |
public DefaultAgentBehavior(Actor<AgentState> actor) | |
{ | |
_actor = actor; | |
} | |
// by a PublicMessageMethodConvention, this methods gets wired up | |
// to the inbox to deliver messages of type A as part of the behavior | |
public void Handle(Message<RequestStockQuote> message) | |
{ | |
_actor.State.RequestCount++; | |
var requestActor = Actor.New<RequestState>(x => x.Apply<InitialRequestBehavior>()); | |
requestActor.Request(message.Body, _actor) | |
.ReceiveResponse<StockQuote>(quoteResponse => | |
{ | |
_actor.State.LastQuote[message.Body.Symbol] = quoteResponse.Body; | |
message.Respond(quoteResponse.Body); | |
}); | |
} | |
// receiving a disable message will change the behavior of the actor | |
public void Handle(Message<UseCache> message) | |
{ | |
_actor.Apply<CacheAgentBehavior>(); | |
} | |
} | |
// another behavior defined for the actor | |
class CacheAgentBehavior : | |
Behavior<AgentState> | |
{ | |
readonly Actor<AgentState> _actor; | |
public CacheAgentBehavior(Actor<AgentState> actor) | |
{ | |
_actor = actor; | |
} | |
public void Handle(Message<RequestStockQuote> message) | |
{ | |
_actor.State.RequestCount++; | |
bool handled = _actor.State.LastQuote.WithValue(message.Body.Symbol, quote => | |
{ | |
_actor.State.CachedRequestCount++; | |
message.Respond(quote); | |
}); | |
if(!handled) | |
{ | |
// we may want to send a temporary unavailable response or something | |
} | |
} | |
// in this case, handling the enable message changes back to the default | |
// behavior | |
public void Handle(Message<UseLive> message) | |
{ | |
_actor.Apply<DefaultAgentBehavior>(); | |
} | |
} | |
class AgentState | |
{ | |
public AgentState() | |
{ | |
LastQuote = new DictionaryCache<string, StockQuote>(); | |
} | |
public int RequestCount { get; set; } | |
public int CachedRequestCount { get; set; } | |
public Cache<string, StockQuote> LastQuote { get; set; } | |
} | |
class RequestState | |
{ | |
public string Symbol { get; set; } | |
} | |
class InitialRequestBehavior : | |
Behavior<RequestState> | |
{ | |
readonly Actor<RequestState> _actor; | |
readonly ISessionFactory _sessionFactory; | |
public InitialRequestBehavior(Actor<RequestState> actor, ISessionFactory sessionFactory) | |
{ | |
_actor = actor; | |
_sessionFactory = sessionFactory; | |
} | |
public void Handle(Message<RequestStockQuote> message) | |
{ | |
_actor.State.Symbol = message.Body.Symbol; | |
// we do some expensive work to get the quote value | |
using(var session = _sessionFactory.OpenSession()) | |
{ | |
var stock = session.Load<Stock>(message.Body.Symbol); | |
message.Respond(new StockQuote | |
{ | |
LastPrice = stock.LastPrice | |
}); | |
} | |
} | |
} | |
class RequestStockQuote | |
{ | |
public string Symbol { get; set; } | |
} | |
class StockQuote | |
{ | |
public decimal LastPrice { get; set; } | |
} | |
class UseCache | |
{ | |
} | |
class UseLive | |
{ | |
} | |
class Stock | |
{ | |
public decimal LastPrice { get; set; } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A more complex example added below the original, this one does a few more things including spawning child actors to handle the requests and a more obvious reason to change behavior (to use a cache when a service is unavailable in this case).