Tag Archives: mvc

This is intended as one of a series of posts on ASP.MVC 3 (Razor) about tips and traps as I learn, feel free to comment with advice or anything to help me or others.

One weird thing I noticed on my MVC form was that it didn’t update input values after a post action if I changed the model value which should be using. It would set the value the first time in the get action, but any subsequent requests would keep the initial value. I had a requirement to update a textbox value to uppercase after a search action but nothing I did in the model would affect the value in the input.

Turns out it was caused by my using an HtmlHelper for my input:

It seems all the HtmlHelpers (dropdownlist/hidden/textarea included) will try to use the posted value if it is present rather than a value from the model, under the assumption that a post which returns to the same view is dealing with validation and should display the same data.

To avoid this you have three options:

·Don’t use a helper, write the input tag and set the value to your model property directly (annoying).

·Use ModelState.Clear() in your controller post action, which will remove all post data and force the helper to use the model (nuke it from orbit! Careful, this will clear all form values

Clear the values you want to update individually, using “ModelState.SetModelValue(key, new ValueProviderResult(null, string.Empty, System.Globalization.CultureInfo.InvariantCulture));” (I added an extension method to do this rather with just the key)

In a previous blog entry I looked at adding persistence to a WF workflow inside an MVC application, using a simple wizard example. This was easy enough but not really a good real world example and discussions about it suggested an expiring password reset token scenario could be a good use of workflow inside a web site. Using a persisted WF workflow would separate the process logic from the MVC controllers and model classes and avoid needing persistence logic for the process instances (expiring tokens). This style of solution could also be used for other website functionality which require persisted workflow actions, such as changing email addresses and retrieving usernames.

Implementation

This example takes the default MVC website and updated the login screen to include a forgot password link, this link leads to a new screen which allows the user to enter their username and submit a password reset request. This starts the workflow, sending an email to the users email address containing a link with an expiring token. If the user clicks the link they are taken to a screen where the token is validated and if it hasn’t expired they can submit answers to their security questions. If they answer correctly their password will be reset and an email with the new password will be sent to them.

The sample project uses the base MVC2 website template, updated with a new PasswordResetController with related model/service objects and views. The controller logic is very simple, merely calling onto the service object to interact with the workflow and depending on the returned result it directs to the required view. The model/service logic is also simple, using a host class for the associated workflow to make calls to the process. The process logic is all contained in the PasswordReset folder, which holds the WF Process Activity, custom Activities, host class and service class. This was placed in the web site for simplicity, but should probably be held in a separate class library with it’s own tests. The service class is there so all the workflow integration logic (sending emails and reseting the password) is handled by an object passed when starting the process, implementing an interface. This allows the workflow to be tested independently of the web site by mocking the PasswordResetService object. The main logic is in the host class which starts/resumes the workflow.

The process itself uses a flowchart activity with custom activities (SendEmailToken etc.) to call onto the integration logic in the service class with the workflow variables. There is a bookmark in the PasswordResetClientActivity which persists the workflow at that point, waiting for client commands. The only oddity is the decision control looping back to the PasswordResetClientActivity when it is completed with the result “Valid”. This is needed when client calls to check if the token is still valid, so the workflow does not complete.

Conclusion

It was very quick and easy to implement this, letting WF handle the process and persistence logic saves a lot of work. The main task was figuring out the tricks in the hosts flow, how to pass in the workflow arguments when starting and action the client activity using the bookmark. It’s a nice simple solution and the separation between the workflow logic and controller logic means changes can easily be made to the process without needing changes to the controller logic. One small disadvantage is the expiring tokens only expire (end the process) at the point they are checked, so it the user does not click the link the process won’t end. Since the workflow is only active when not idle this only means old instances will leave a couple of rows in the instance store DB, which shouldn’t be an issue and can be cleaned up if necessary. If you wanted a more complicated workflow or needed better auditing/error handling you could host the workflow in a WCF service instead and make use of Windows Server AppFabric.

Writing this as lately I’ve been interested in WF4.0 (it’s always pronounced “dub-F” in my head) and saw a post about using WF with MVC to handle a site registration wizard. The tutorial it linked to used a state machine (WF only supports Sequential OOB) workflow without persistence, which made me think that you could easily use a normal workflow with persistence to do a lot of other things inside a web application. WF which is part of .NET4.0 allows you to easily create and call workflow processes, small or large, from anywhere inside .NET code and it’s been drastically improved since .NET3.5. That ease makes it practical to use workflow in places which it was previously too complicated to integrate, places where we normally manually code a process into business or web logic making it difficult to maintain and add standard features like tracing/auditing.

To run the sample or try this yourself you will need .NET4.0 and Visual Studio 2010, the instructions below are a simple overview and do not include all code or references.

1. Creating the WF SQL Persistence store

WF4.0 supports using a default SQL Persistence store, using a DB created using scripts in the .NET4.0 framework directory. Simply create a new DB (call it whatever you like) on your server and run the following scripts:

Multiple workflows can share the same DB or you can separate them out if you want.

2. Creating the MVC site and Workflow Activity Library

In Visual Studio, create a new MVC Web Application and add another project of type “Activity Library” (under templates Workflow) to hold your workflow. I also added a console application to test the workflow independently but this is not necessary.

3. Creating the Workflow

Delete the existing workflow activity and create a new one with the name WizardActivity, in the activity drag and drop a Flowchart from the toolbox onto the workflow (using this instead of a state machine) and add a variable “Outcome” to the Flow chart scope (used to store the outcome Next/Back of the steps). In the Activtiy project add a new Class, WizardStep, which will be generic custom activity used for all the steps in the workflow. This activity contains the bookmark code which is used to halt and persist the workflow at the steps. Add the code and compile the project, this will update the workflow toolbox adding WizardStep and allowing you to drag and drop it onto the workflow. Add three Wizard steps to the workflow, setting the Name to “StepN“, Bookmark Name to “StepN” and Input to “Outcome” (this is an output parameter which will set Outcome to the value supplied when resuming the Bookmark in the custom activity). Add a custom Complete activity (which does nothing) and link up the workflow using Decision controls (set condition to “Outcome.Equals(“Next”)”) and arrows as per image. Lastly add a new Class WizardHostHelper (copy code from sample), which acts as the host for the workflow; creating the Workflow application object, setting instance store values, starting/resuming the workflow, actioning the bookmarks.

4. Update the MVC application to use the Workflow

Now to integrate the workflow into the website.

Add a new class WizardModels, to hold the model logic for the wizard steps and workflow service (wrapper to WizardHostHelper).

Add a new controller WizardController, for controller logic used in the wizard.

Add four new views under Wizard, Step1-3 and final, for the views used in the Wizard.

Update AccountModel, add a Guid parameter workflowKey to the MembershipService.CreateUser method, this will store the Workflow Instance key in the standard ASP user security providerUserKey. Also add a method GetUserWorkflowKey to return the key for a username.

Update AccountController, changing Register method to start a workflow instance for the user using the WizardModels.WorkflowService and supply the workflow key into CreateUser. Update both Register and Logon to redirect action to the Wizard index.

Update the web.config to set the instance store connection string.

And that should be it, registering a new user will now start the workflow process and redirect to the first step. Next/Back buttons on the step views cause the controller to action the steps, progressing the workflow. The workflow is persisted to the DB at each step, so even if the website goes down the current step of the user in the workflow is maintained. The workflow, rather than the controller, decides which step the website should display, separating the process logic from the controller logic.

There is very little code involved, just small service/host classes used to access the workflow and integrate with the website. To add persistence it only really took two lines of code in the host class (setting the workflow application instance store and telling it to persist when idle at the bookmarks), compared to the tutorial sample host. The workflow used could be much more complicated, branching to different steps depending on user response (e.g. redirecting to a different address screen if the user isn’t based in the UK) without adding complexity to the controller logic. As the workflow is separated from the website it can be tested independently (no website needed! automated testing!) and updated without large changes needed in the website. It’s also a very light weight (and cheap) way to add workflow, which is something we may need going forward. I’m currently looking at ways now to use WF with persistence in Azure cloud (without the upcoming Azure AppFabric) for simple workflow solutions, as right now we can’t use our current workflow applications like K2.