Sample Portable Area
This is the second part in a series about creating a Portable Area (PA) using MvcContrib
To create a Portable Area, the first step is to create a separate class library project for the Portable Area. The output assembly is the single file needed to add this functionality to applications which wish to consumer it.
The minimal references required for the Portable Area are shown below:
While DataAnnotations is not required to build a PA, using DataAnnotations for Model validation makes distribution of the PA very easy, since the MVC2 DefaultModelBinder supports the DataAnnotations validation out of the box. If you choose a different validation framework than you would need to distribute those assemblies or possible IL Merge them into your PA.
The sample PA in the MvcContrib project is a Login Portable Area, this is a pretty simple example, but demonstrates how a PA and application can integrate. This is also a piece of functionality that most applications need.
Below is the code listing and structure for the project. It looks identical to a mulit-project ASP.Net MVC Area.
The first difference between creating a Portable Area and a standard area from the files perspective is how the Views are registered with the project. The views Build Action should be changed from content to an Embedded Resource. This allows the views to be embedded into the output assembly so that they view folders and files to not have to be manually copied into each application that uses it. Instead a special view engine will pull these views out of the assembly at run time and render them the same way that a physical view would be rendered.
The second item that is needed to create a Portable Area is a specialized registration class for the that inherits from PortableAreaRegistration. This class provides an extended RegisterArea method that sends in the MVC AreaRegistrationContext and a IApplicationBus object to the method. The IApplicationBus is the mechanism that a Portable Area uses to send messages to the hosting application during. The use of the bus during registration is not required, but it is available in case and specific start up logic is needed to correctly configure the Portable Area.
The last line of the method show how the Portable Area registers the embedded views with the view engine, using the call to the RegisterTheViewsInTheEmmeddedViewEngine method.
An important caveat around the setup of the Registration class is to make sure that the Registration class is sitting in the same Namespace as the controllers and views. If the Namespaces do not match the views will not be able to be located. Since the namespace for the Embedded Resource views cannot be specified declaratively they are created using the Assemblies Default Namespace and the folder structure that they are stored in. So keeping all of these properties consistent will make everything work. If you change the default namespace of the project, you will need to update the namespace of your registration and controller classes.
The first call in this registration method sends a message to the hosting application. This is a contrived example but it demonstrates sending a tracing message that can be used by an observer message handler to log the registration activity. This tracing can be useful since the Portable Areas use the built in MVC registration mechanism for auto discovery and there is not a lot of visibility into the built in registration process. This example shows how a message can be sent between the PA and the hosting application.
The LoginController demonstrates the functionality needed to handle displaying the login form and then handling the form post. There are two Actions in the controller, one to render a Login View and a second Action to process the form post and redirect the user on success or , in the case of a login failure, display the login error messages to the user.
The message bus is the main mechanism for communicating between a portable area and the hosting application. This is a very simplistic mechanism that allows the Portable Areas to communicate with the hosting application in a synchronous manner.
For some scenarios, like when the area should not implement its own database, the host application can own how the domain model is persisted in data storage and the Portable Area can worry about handling the multi-page user interface and other user interface concerns like validating simple data types.
The messages that are passed to the Bus are shown below.
The LoginInput represents the data that is collected from the Login screen. It is using the built in DataAnnotations attributes to handle the simple validation rules.
The result object is used to determine if the credentials were valid for the host application and are then used to either display the error message or redirect to a protected page. Since the LoginInputMessage is passed to the bus with a reference, the handlers in the host application can change the Result object’s state and the portable area can than access the Result and determine the correct logic to perform given that result. The implementation for the handlers will be in the next post in this series.
The Index view for the login uses the MvcContrib Input Builders to render a standard form to the page. The use of the Input Builder allows an host application to override any of the html mark up using the Input Builder partials. This means that the Portable Area and Input Builders play very nicely with each other.
This is what the view looks like in a browser.
This is the validation errors that are handled by the Portable Area, using the MVC 2 default model binder.
This shows an error message that is passed back from the resulting application. Other rules could be applied as well.
This is the approach for creating a Portable Area (PA) using the MvcContrib Portable Area feature. Let me know what you think about this approach, we tried to stay with the simplest approach that could possible work, and focused on making it easy to both develop and consume the Portable Areas.
Please leave your comments on what you think of the approach.