Inspecting contents of UnitOfWork

Posts   
 
    
Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 04-Jan-2005 07:45:10   

Are the entities and collections being tracked by the unit of work accessible at a later time?

In asp.net I am developing a wizard framework. Developers chain together a series of non-related / stand alone web user controls (wizard steps). Step 1 might be to fill in a customer entity, step 2 would be fill in an address entity etc. There is a state manager framework created using the State design pattern that is used by the wizard host to determine where the user is in the wizard workflow. The wizard host loads each wizard step as the user navigates through the step. Prior to leaving the step the host signals the step that it is time to save its state and move on.

So, I was thinking that the wizard host could have an instance of a unit of work object, because all wizard steps can get to the wizard host via the state manager. When the host tells the step to save it's state, I can add the entity for the step to the unit of work.

For example, say I go to step 1, fill in the controls, and click next. Step 1 creates a new customer entity and adds it to the unit of work. I realize that my customer info is incorrect so I click back. Now I want to check the unit of work for an instance of the customer entity. If the entity exists, I want to use it to re-populate my web controls in step 1. Is this feasable or should I just create a wrapper for this operation? I checked the LLBLGen help and the reference guide and I did not see any accessors for entities and collections that it was tracking.

Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 04-Jan-2005 10:22:02   

The UnitOfWork classes are meant for keeping track of actions you have to do, not as a bucket of entities you work with (as entities which you read data from are not part of the UnitOfWork so using the UnitOfWork for that doesn't make sense in that regard, IMHO). So because of that, I didn't add any code to access the added elements, because you have the references to the entities anyway also outside the UnitOfWork. If you want these accessors, I'll add them

Frans Bouma | Lead developer LLBLGen Pro
Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 04-Jan-2005 10:41:09   

Otis wrote:

The UnitOfWork classes are meant for keeping track of actions you have to do, not as a bucket of entities you work with

An example of this would be creating an instance of a CustomersEntity, setting its values to the values collected in the UI, and then putting it into the UOW for a Save operation, correct?

In my scenario, I am creating new entities in each step of the wizard, filling the entity values with values collected from the UI, and passing the entities to the UOW.

I was just thinking it would be convenient to be able to see what the UOW knows about so that I can fill in the forms controls using values from the entities already contained in the UOW, that makes logical sense right?

In any case, I can just add the new entities to a hash table and have the step check the hash table for its entities, and pull the values that way.

If I create a new entity, then set its values, will I be able to read the values without performing a save or will I get an ORM Exception? i.e.

 CustomerEntity newCustomer = new CustomerEntity();
newCustomer.FirstName = "bob";
newCustomer.LastName = "jones";

textBox1.Text = newCustomer.FirstName;
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 04-Jan-2005 11:44:56   

The problem might be that finding back an entity in the UnitOfWork is a bit hard, for example with new entities with an identity PK... they all have 0 as a value. (ok, you can set it to a dummy value, but you then have to keep track of which id belongs to which entity, effectively doing the same as keeping track of the real entity anyway).

An entity has as state 'EntityState.New', and shouldn't give a problem with reading values back, as that would give issues with grids where people fill in values and add rows. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 04-Jan-2005 17:32:55   

So when using the UOW object, lets say I add 10 entities to the UOW for a commit operation, and I realize that I made a mistake on the 3rd entity added. I cannot remove or replace this 3rd entity?

I have to Reset the UOW and go through steps 1-10 again?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 04-Jan-2005 18:19:57   

Yes, and that can be a problem, however the unitofwork is nothing more than a collector of actions you would have executed on the db directly otherwise, which brought no 'undo' unless you rolled back the transaction.

I understand the necessity for 'maintenance' methods on the unitofwork, as you've described it nicely, so I'll add those simple_smile (so the purpose of the UoW is extended, it's more useful in more scenario's).

Frans Bouma | Lead developer LLBLGen Pro
Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 05-Jan-2005 19:19:22   

Otis wrote:

(ok, you can set it to a dummy value, but you then have to keep track of which id belongs to which entity, effectively doing the same as keeping track of the real entity anyway)

If I do set a dummy value in the PK field, will the dummy value be replaced by the "Real" identity value on save?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 05-Jan-2005 19:23:47   

yes simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 06-Jan-2005 04:34:25   

OMG, I finally got my framework stable and running. Its been a roller coaster week.

I implemented a Hashtable in my StateManager to hold onto llblgen entities from step to step and it worked out quite well. I have a StateManager that holds a hash table and a UOW2 object. The state manager is passed from State to State and from Step to Step. So each step in the wizard has access to the state manager, the hash table, and the UOW.

Each step in the wizard works with the entities that it needs to and puts them into the hashtable. Other steps may get at the hashtable to use the entities already in it. At the end, all entites to be saved are plucked from the hashtable and put into a UOW2 object. The UOW2 is passed to a controller and the commit is made.

Basically, that is where I was going with this whole deal. If you want to see the code I would be happy to share it. It would be cool if I couldve just used a UOW2 instead of a Hashtable and UOW2.

Thanks for the help Frans, and I look forward to your extension of the UOW2 object.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 06-Jan-2005 09:23:16   

Sounds interesting. simple_smile If it's not too much you could post the code here simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 06-Jan-2005 10:38:47   

Devildog74 wrote:

Basically, that is where I was going with this whole deal. If you want to see the code I would be happy to share it. It would be cool if I couldve just used a UOW2 instead of a Hashtable and UOW2.

Sounds great... out of interest, why didn't you extend UOW and override the AddForSave and AddForDelete methods?

Devildog74
User
Posts: 719
Joined: 04-Feb-2004
# Posted on: 06-Jan-2005 14:16:10   

Marcus wrote:

out of interest, why didn't you extend UOW and override the AddForSave and AddForDelete methods?

I suppose I could have. Maybe the idea hadnt come to me, but now it does sound interesting. Since there is no way to get an item out of the UOW after its been added any many items may be added, how or when would you "AddForSave" the entity to the UOW? I think I might have considered it but couldnt see how inheritance wouldve helped so I decided to make the implementation outside of the UOW and leverage the UOW as it sits in the end of the process.

Here is a sample from a wizard step. In this step, the user is filling in info setting up an account


        private void Page_Load(object sender, System.EventArgs e)
        {       
            stateManager = Session["WizardStateManager"] as WizardStateManager;
            lblStepName.Text = stateManager.CurrentWizardStep.StepName;

            InitializeUsingStepState();
            ClearStepState();

        }

        public void SaveStepState()
        {
            if (Page.IsValid)
            {
                newAccount = new AccountEntity();
                newAccount.FirstName = txtFName.Text;
                newAccount.LastName = txtLName.Text;
                newAccount.EMail = txtEmail.Text;
                newAccount.Password = txtPassword.Text;

                stateManager.StepStateValues.Add(NewAccountKey, newAccount);
                Session["WizardStateManager"] = stateManager;
            }
        }

        public void ClearStepState()
        {
            if (stateManager.StepStateValues.Contains(NewAccountKey))
            {
                stateManager.StepStateValues.Remove(NewAccountKey);
            }
        }

        public void InitializeUsingStepState()
        {
            if (stateManager.StepStateValues.Contains(NewAccountKey))
            {
                newAccount = 
                    stateManager.StepStateValues[NewAccountKey] as AccountEntity;

                txtEmail.Text = newAccount.EMail;
                txtFName.Text = newAccount.FirstName;
                txtLName.Text = newAccount.LastName;
                txtPassword.Attributes.Add("Value", newAccount.Password);
                txtConfirmPassword.Attributes.Add("Value", newAccount.Password);

            }
        }

Here is the last step object being managed by my state manger:


    public class LastStep : WizardStateBase
    {

        public override void Finish(object wizardState)
        {
            WizardStateManager stateManager = wizardState as WizardStateManager;
            if (stateManager != null)
            {
                if (stateManager.StepStateValues.Count > 0)
                {
                    stateManager.ActionsToSave.AddForSave((AccountEntity)
                        stateManager.StepStateValues[Step1.NewAccountKey]);

                    stateManager.ActionsToSave.AddForSave((AccountAddressEntity)
                        stateManager.StepStateValues[Step2.ShippingAddressKey]);

                    stateManager.ActionsToSave.AddForSave((AccountAddressEntity)
                        stateManager.StepStateValues[Step2.BillingAddressKey]);

                    stateManager.ActionsToSave.AddForSave((AccountProfileEntity)
                        stateManager.StepStateValues[Step3.NewAccountProfileKey]);



                    AccountCustomController controller = (AccountCustomController)
                        ControllerFactory.GetController(
                        ControllerFactory.AccountCustomController);

                    controller.CommitNewAccountWizard(stateManager.ActionsToSave);

                }

                stateManager.ActionsToSave.Reset();
                stateManager.StepStateValues.Clear();
                stateManager.SetState(WizardStateBase.WIZARD_COMPLETE);
            }
            
        }
}

Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 06-Jan-2005 15:55:21   

Actually I just checked... UnitOfWork2 does not mark all it's methods as virtual... Frans is this a bug?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 06-Jan-2005 17:04:03   

Piggybacking methods (which call into one method) are not virtual. This is MS' best practises. stuck_out_tongue_winking_eye . If you've found a method which should be virtual but isn't, let me know.

Frans Bouma | Lead developer LLBLGen Pro
Marcus avatar
Marcus
User
Posts: 747
Joined: 23-Apr-2004
# Posted on: 06-Jan-2005 18:43:27   

Otis wrote:

Piggybacking methods (which call into one method) are not virtual. This is MS' best practises. stuck_out_tongue_winking_eye . If you've found a method which should be virtual but isn't, let me know.

public bool AddForDelete(IEntity2 entityToDelete, IPredicateExpression restriction)

stuck_out_tongue_winking_eye

[edit] And public virtual bool AddForSave(IEntity2 entityToSave, IPredicateExpression restriction, bool refetch)

is virtual and shouldn't be... stuck_out_tongue_winking_eye x2

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39933
Joined: 17-Aug-2003
# Posted on: 06-Jan-2005 20:32:43   

good catch! simple_smile Will change that in the next upgrade. simple_smile

Frans Bouma | Lead developer LLBLGen Pro