State Pattern and CSLA.NET

State Pattern and CSLA.NET

Old forum URL: forums.lhotka.net/forums/t/5710.aspx


czuvich posted on Thursday, October 30, 2008

I am trying to implement the state pattern with CSLA; however, I am running into a few issues with it.

The state pattern basically involves a circular reference between the state itself and the object consuming it (the context) that way the state object can change the context's state on the fly (at least that's my understanding of how it works.. please correct me if I am wrong).

If I use a managed field (RegisterProperty) to hold the reference to the context inside of the state object, the DataPortal_Create throws a stack overflow exception (I'm assuming because of the circular reference). If I use a private field to hold the context reference, property change notifications aren't fired.

Could someone please explain to me how I might need to implement the state pattern with CSLA? Basically, my abstract State class (StateBase) has a factory method that calls the DataPortal.Create<ConcreteClass> of the concrete state.

In the DataPortal_Create of the context class, it calls the factory method of the abstract StateBase.  Should the concerete state classes be children of the context? .. Any advice would help. Thanks.

RockfordLhotka replied on Thursday, October 30, 2008

I don't have a great mental picture of the State pattern.

But if you are writing code that calls DataPortal_XYZ methods yourself, then you are in trouble, because that's not how the data portal is designed to work.

If you want a high level of control over how your objects are created/loaded/saved/etc then you should look at the ObjectFactory concept in version 3.6. In that model the data portal invokes a factory object that you create, and your factory object is responsible (completely!) for instantiating and interacting with your object graph.

czuvich replied on Friday, October 31, 2008

Hmm... I thought I was making correct calls to the DataPortal, but perhaps that is a problem with my code. I'll have to look back and see how object composition is supposed to be handled. Essentially here's a sample of the state pattern and how I am using it with CSLA.  Perhaps this code block will help. The syntax may not be spot on.

 

[System.Serializable()]
public abstract class StateBase : BusinessBase
{
   /* some properties.. cut out */
   // the reference to the context object
   [NotUndoable()]
   private static PropertyInfo MyContextProperty = RegisterProperty(typeof(StateBase), new PropertyInfo(MyContext>("MyContextAccessor"));
   
   protected MyContext MyContextAccessor
   {
      // normal CSLA ReadProperty/LoadProperty
   }

   public abstract void ApproveSomething();

   // some validation rules
   protected StateBase() {}
   public static StateBase NewStateBase(MyContext ctxt)
   {
      return DataPortal.Create<ConcreteStateA>(new SingleCriteria(ctxt));
   }  
}

[System.Serializable()]
public class MyContext : BusinessBase
{
   // private reference to the state object... properties are public that access this
   // object's properties.. for example.. 
   /*
      public string Prop
      { get { return this.internalState.Prop; } }
   */
   private StateBase internalState;

   public override bool IsDirty
   {
      get { return base.IsDirty || this.internalState.IsDirty; }
   }

   public override bool IsValid
   {
      get { return base.IsValid && this.internalState.IsValid;
   }

   public override BrokenRulesCollection BrokenRulesCollection
   {
       get { // create a new collection, and merge the internal state with this graph }
   }
  
   public static MyContext NewContext()
   {
      return DataPortal.Create();
   } 
 
   [RunLocal()]
   protected override void DataPortal_Create()
   {
      this.internalState = StateBase.NewStateBase(this);
      this.ValidationRules.CheckRules();
   }
}

[System.Serializable()]
public class ConcreteStateA : StateBase
{
   public override void ApproveSomething()
   {
      // approve, then call ChangeStates()
   }

   private void ChangeStates()
   {
      this.MyContextAccessor.internalState = ConcreteStateB.NewStateB(this);
   }
  
   [RunLocal()]
   private void DataPortal_Create(SingleCriteria ctxt)
   {
      this.MyContext = ctxt;
      ValidationRules.CheckRules();
   }
}

czuvich replied on Friday, October 31, 2008

The issue I am having is when the DataPortal is called I receive a StackOverflow exception.  I've tried marking the ConcreteState classes as children (using the DataPortal.CreateChild operations); however, I seem to get null for the Parent.

Should I be making a second call to the dataportal for objects inside of other objects or should I just use a constructor? THanks.

RockfordLhotka replied on Friday, October 31, 2008

You can just use a constructor to load child objects. Though I generally recommend using a factory method approach, as it provides better abstraction overall. Look at ProjectTracker from version 2.0 to 3.0 to see examples of using direct method calls (to a factory or constructor).

 

Also, if you are trying to create a pure state container to hold the state of your object, look at the field manager in version 3.5 and higher. The field manager is a state manager that manages the state of the object in a separate object (namely the field manager). There are some very interesting quirks to getting this to work with n-level undo, serialization and eventing.

 

Rocky

czuvich replied on Monday, November 03, 2008

I'll look into the FieldManager class and see if it will suit my needs; however, I am storing other fields besides the state of the object.  I am also storing fields that are related to its state.  Essentially, I wanted a structure that allowed business rules to be defined on a per state basis, but I did not want to use a strategy pattern since the client isn't really aware of the different strategies.

I'll keep toying with the state pattern and CSLA and keep this forum updated. If anyone has had success implementing this pattern with CSLA, feel free to post if you've ran into the same quirks.

RockfordLhotka replied on Monday, November 03, 2008

Whether FM meets your needs or not I don't know. I was suggesting you look at it primarily because it illustrates the issues you'll need to address to create a "child" object that isn't really a child, but rather is the state container for an object.

Specifically, you'll need to fully participate in the n-level undo process by implementing IUndoableObject, making sure to properly honor the parentBindingEdit parameter.

If you want to integrate with CSLA 3.6 (for Silverlight at least) you'll also need to implement IMobileObject so you work with the MobileFormatter.

czuvich replied on Tuesday, November 04, 2008

Thanks for your assistance and guidance. I'll look into the FM and see how you're currently handling state within another object.  I'll try to keep this forum posted and put up a successful implementation of the State pattern using CSLA.NET. Thanks again.

Copyright (c) Marimer LLC