Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

State Pattern with Entity Framework

I have a model Enquiry, which can be in one of two states (there are more but for the purposes of this I will just compare two): New and Closed. The state the enquiry is in is dependant upon what a user is able to do with an enquiry. For example a closed enquiry cannot be deleted where as a new enquiry is able to be deleted and so forth (basic example).

I am wanting to persist this with Entity Framework but not sure how. Below is my code.

Enquiry:

public class Enquiry
{
    public int Id { get; set; }
    public string CustomerAccountNumber { get; set; }

    public EnquiryState CurrentState { get; set; }
    public bool CanAddLines { get { return CurrentState.CanAddLines; } }
    public bool CanDelete { get { return CurrentState.CanDelete; } }

    public void ChangeState(EnquiryState currentState)
    {
        CurrentState = currentState;
    }

    public void CloseEnquiry()
    {
        CurrentState.CloseEnquiry();
    }

    /* More methods to change state here */

    public Enquiry()
    {
        CurrentState = new NewEnquiryState(this);
    }
}

EnquiryState:

public abstract class EnquiryState
{
    internal readonly Enquiry CurrentEnquiry;

    protected EnquiryState(Enquiry currentEnquiry)
    {
        CurrentEnquiry = currentEnquiry;
    }

    public virtual bool CanDelete
    {
        get { return false; }
    }

    public virtual bool CanAddLines
    {
        get { return false; }
    }

    /* More properties here */

    public abstract void CloseEnquiry();

    /* More states here */
}

NewEnquiryState:

public class NewEnquiryState : EnquiryState
{
    public NewEnquiryState(Enquiry enquiry) : base(enquiry) { }

    public override bool CanDelete
    {
        get { return true; }
    }

    public override bool CanAddLines
    {
        get { return true; }
    }

    /* ... */

    public override void CloseEnquiry()
    {
        CurrentEnquiry.ChangeState(new CloseEnquiryState(CurrentEnquiry));
    }

    /* ... */
}

CloseEnquiryState:

public class CloseEnquiryState : EnquiryState
{
    public CloseEnquiryState(Enquiry enquiry) : base(enquiry) { }

    public override bool CanAddLines
    {
        get { return false; }
    }
    public override bool CanDelete
    {
        get { return false; }
    }

    /* ... */

    public override void CloseEnquiry()
    {
        throw new Exception("Closed Enquiry can't be closed");
    }
}

So my question is, I'm not sure how to store these different states in a database, should I use some sort of int field on each state and map them to the Enquiry via an FK? Also, do I need to map the fields CanAddLines and CanDelete to a database too? Seeing as the logic is contained within the state, quite new to the state pattern paradigm

like image 315
CallumVass Avatar asked Oct 03 '22 16:10

CallumVass


1 Answers

Your state does not have any data to be stored. So, actually you need to store only state type:

[NotMapped]
public EnquiryState CurrentState { get; set; }

public int StateType
{
    get 
    {
       // get value based on CurrentState
       return (CurrentState is NewEnquiryState) ? 0 : 1;
    }
    set
    {
        // create EnquireState based on value
        CurrentState = value == 0 ? 
            (EnquiryState)new NewEnquiryState(this) : 
            (EnquiryState)new CloseEnquiryState(this);
    }
}

BTW you don't need to override abstract class virtual members if they are already return what you need (e.g. CanAddLines and CanDelete properties of CloseEnquiryState)

like image 106
Sergey Berezovskiy Avatar answered Oct 12 '22 12:10

Sergey Berezovskiy