Introduction
The article introduces an innovative architecture to develop web forms in
enterprise software rather than either ASP.NET or MVC through step by
step comparison on development complexity, reusability, performance and
maintainability. The architecture is implemented as an important UI
component of RapidWebDev which is an open source enterprise software
development infrastructure.
RapidWebDev Uri: http://www.rapidwebdev.org
Catalog of The Article
1. Development Complexity Comparison
Problems of ASP.NET and MVC development
The complexity of ASP.NET and MVC development is that we have to write
new ASP.NET templates for different web forms although we can summarize
the main functionality of web forms are query, list, detail and any
other custom operations. But in most case, we don't have the same
requirement on query filters, grid columns, detail panel or any other
operation panel surface between web forms. So master page doesn't work
for such scenarios. So with more code written for different web forms,
1.1.
we're hard to consolidate the style of web forms in a system especially
in teamwork, e.g. indent, label style, operation notificaton, user
interaction behavior etc.
1.2. we have to handle each postback or submit manually. e.g.
- 1.2.1 in a submit of query, we have to go through each query
filter one by one and assemble it with query expression if a query
filter is not empty like the code snippet below. The hardwired query
expression is not agile for changes. We have to re-compile the solution
and deploy it even if we have a small change on a query filter.
if (!string.IsNullOrEmpty(this.TextBoxQueryByName.Text.Trim()))
q += q.Where(user => user.Name.Contains(this.TextBoxQueryByName.Text.Trim()));
if (!string.IsNullOrEmpty(this.TextBoxQueryByName.Text.Trim()))
{
string nameParameter = this.TextBoxQueryByName.Text.Trim().Replace("'", "''");
q += "AND Name LIKE '%" + nameParameter + "%'";
}
- 1.2.2 when we need to support paging and sorting in grid, we
have the similar issue as query. We have to write different handlers
for an user submits an either query, paging or sorting. And in the
handlers, we also need to assemble query expression.
1.3. we're hard to handle and consolidate UI interaction bebaviors
manually like the following simple code snippet. The more complex
scenario is when an user selects multiple records in grid and click
bulk delete button, a modal confirmation panel should be displayed. In
the case, we have to implement the modal panel by JS code. The such
code increases the complexity of UI obviously especially in teamwork
that each member may implement behaviors in different ways.
protected void OnQueryButtonClick(sender object, EventArgs e)
{
Grid.Visible = true;
DetailPanel.Visible = false;
ApprovePanel.Visible = false;
}
protected void GridView_RowCommand(object sender, GridViewCommandEventArgs e)
{
if (string.Equals("Edit", e.CommandName, StringComparison.OrdinalIgnoreCase))
{
DetailPanel.Visible = true;
ApprovePanel.Visible = false;
}
}
1.4. UI code becomes more complex when we integrate
actions with permission in a web form, like readonly, create, update,
delete and approve permission etc. We have to hard code somewhere in
the webform to redirect or hide some actions based on authorization of
current user. We may abstract the redirect from a HttpModule or base
page class, but that permission only works for page level. We have no
way to authorize on behavior level besides a sample dummy code snippet
as following.
protected void Page_Load(object sender, EventArgs e)
{
if (user is not authorized)
redirect to Unauthorized page;
if (user has no readonly permission)
redirect to Unauthorized page;
if (user has permission on create)
display create button;
if (user has permission on update/delete)
display update/delete button in each row of grid;
if (user has permission on approve)
display approve button somewhere;
if (user has permission on OperationX)
display OperationX button somewhere;
}
1.5. nearly impossible to write unit tests for ASP.NET
pages and controls that we're hard to feel free for any coming changes
in UI. This is not the issue for MVC.
Solutions of RapidWebDev UI Framework
RapidWebDev UI framework is in a different thought to ASP.NET or MVC
development. The framework predefines general behaviors of web forms
include both UI panel visibility control and user actions which can be
summerized as following,
- when an user clicks button Query, the data returned asynchronously from server is rendered to grid.
- there are three buttons configurable for each row of grid, View, Edit and Delete. When an user clicks button View or Edit,
the record is rendered in a modal detail panel. When the user saves
changes of the record, the modal detail panel is closed and the grid is
refreshed automatically. When an user clicks button Delete, the record is deleted after the user confirms deletion in a popup dialog.
- when an user clicks button Add in Button Panel, a
blank modal detail panel is popup for input. When the user saves
changes of the record, the modal detail panel is closed and the grid is
refreshed automatically.
- when an user clicks a custom button in Button Panel, the button related Aggregate Panel
is popup in a modal dialog. The user can confirm the custom operations
in the aggregate panel against the records selected in grid. After the
user confirms the custom operation against selected records, the modal
aggregate panel is closed and the grid is refreshed automatically.
When an user does an operation in UI, the framework invokes the
operation related callback method of configured implementation of
interface IDynamicPage, IDetailPanelPage or IAggregatePanelPage. The
architecture as following,
So let's see how RapidWebDev UI framework improves the facing problems of ASP.NET and MVC,
1.1.
the UI of web forms are rendered by the framework through xml
configuration and template. So UI style of web forms in a software such
as label, indent, layout are easy to be uniform that the framework
renders UI from xml by . And the user interaction is predefined and
managed by the framework so that user interaction behaviors of web
forms are consolidate out of question.
1.2. query filters are configured by xml. When an user clicks Query button, the framework callbacks method Query with QueryParameter collected from click submit automatically. QueryParameter
includes query filters, sorting and paging information which can be
converted to query expression intelligently. So we don't need to
assemble query expression manually as the following code snippet. With
this design, we're very agile for changes of query filters that we
don't need to change any CLR code but only modify the xml configuration.
public override QueryResults Query(QueryParameter parameter)
{
using (ProductManagementDataContext ctx =
DataContextFactory.Create<ProductManagementDataContext>())
{
IQueryable<Product> q = from p in ctx.Products
where p.ApplicationId == authenticationContext.ApplicationId
select p;
LinqPredicate predicate = parameter.Expressions.Compile();
if (predicate != null && !string.IsNullOrEmpty(predicate.Expression))
q = q.Where(predicate.Expression, predicate.Parameters);
if (parameter.SortExpression != null)
q = q.OrderBy(parameter.SortExpression.Compile());
int recordCount = q.Count();
var results = q.Skip(parameter.PageIndex * parameter.PageSize)
.Take(parameter.PageSize).ToList();
return new QueryResults(recordCount, );
}
}
1.3. we're just writing business logics in callback for
predefined or custom operation but not take care of how and when the
callback is invoked by the framework. So we don't have the troubles on
control visibility of panels manually as ASP.NET or MVC. Take an
exampe, we implement a callback for Create as following sample code
snippet. We don't need to take care of how to switch the panels after
the record been created. Generally, the sight on developing web forms
is limited so that the complexity is decreased. Finally the
developlment velocity and product quality is increased.
public override string Create()
{
ConcreteDataObject concreteDataObject = new ConcreteDataObject();
concreteDataObject.Name = this.TextBoxName.Text;
concreteDataObject.Value = this.TextBoxValue.Text;
concreteDataObject.Description = this.TextBoxDescription.Text;
concreteDataApi.Save(concreteDataObject);
return concreteDataObject.Id.ToString();
}
1.4. Authorization is integrated in the framework
through interface IPermissionBridge which is configured in Spring.NET
IoC. Take an example,
ProductManagement is configured for a web form.
ProductManagement.Add is the permission for adding a new product.
ProductManagement.Update is the permission for editing an existed product.
ProductManagement.[CommandArgument] is the permission for special button and aggregate panel with that command argument. If an user don't have permission
ProductManagement.Add, the button
Add is invisible for the user and the related method
Create
in the implementation of interface IDetailPanelPage is protected. So we
don't need to write explicit permission checking code any more.
1.5. Webforms developed by RapidWebDev UI framework is unit testable. Let's see the test case for the method
Create above.
[Test, Description("Test the Create Method in ConcreteDataDetailPanel")]
public void TestCreate()
{
ConcreteDataDetailPanel page = new ConcreteDataDetailPanel();
DetailPanelPageProxy proxy = new DetailPanelPageProxy(page);
using (var httpEnv = new HttpEnvironment())
{
httpEnv.SetRequestUrl(@"/ConcreteDataDetailPanel/DynamicPage.svc?ConcreteDataType=Department");
#region Set binding controls of DetailPanel
TextBox TextBoxName = new TextBox();
TextBoxName.Text = "name";
proxy.Set("TextBoxName", TextBoxName);
TextBox TextBoxValue = new TextBox();
TextBoxValue.Text = "value";
proxy.Set("TextBoxValue", TextBoxValue);
TextBox TextBoxDescription = new TextBox();
TextBoxDescription.Text = "description";
proxy.Set("TextBoxDescription", TextBoxDescription);
#endregion
string entityId = proxy.Create();
IConcreteDataApi concreteDataApi = SpringContext.Current.GetObject<IConcreteDataApi>();
ConcreteDataObject obj = concreteDataApi.GetById(new Guid(entityId));
Assert.AreEqual(obj.Name, "name");
}
}
2. Reusability Comparison
Problems of ASP.NET and MVC development
The
ASP.NET developed web pages are hard reusable. We cannot inherit a web
page from another although there may be a lot resuable stuff. MVC
decouples controller and view but the panels inner of the view are
mixed together. Once we developed a web form with query, list, edit,
view or any other panels for custom operations, even we're hard to
reuse this form to display a single record only. Take an example, we
have an user management page. If we want to see the profile of an user
in system, we cannot reuse the page to display the profile of user only
in other places because there are query panel, grid etc.
Solution of RapidWebDev UI Framework
With
RapidWebDev UI framework, we implement interface IDynamicPage and write
xml configuration for query and list; implement interface
IDetailPanelPage and write an ascx template for detail panel; implement
interface IAggregatePanelPage and write ascx template for aggregate
panel. The development of a web form is broken down into multiple
units. This enables us possible to reuse small units of a web form in
other place. "Reusability" in RapidWebDev UI framework can be
summerized as,
2.1. reuse detail panel for a single record view
If we developed a product management page at Uri: ~/ProductManagement/DynamicPage.svc,
We can reuse the page for detail information view by Uri: ~/ProductManagement/DetailPanel.svc?entityid=792db4cc-3a89-4f9d-94e8-a9293cd27c56&rendermode=View. This actually reuses the detail panel of a whole web form to view a single record directly.
2.2. reuse implementations of interface IDynamicPage, IDetailPanelPage
or IAggregatePanelPage in other web forms. If there are two web forms
having the similar functionality requirement but different layout, we
can only configure different xml or ascx template but using the same
implementations of interface IDynamicPage, IDetailPanelPage or
IAggregatePanelPage.
2.3. extend implementations of interface
IDynamicPage, IDetailPanelPage or IAggregatePanelPage by inheritence.
This is quite useful in an enterprise software that a business workflow
is usually composed of a series of processes to an entity. Like in a
shiping order workflow, we may have the processes as create, approve,
distribute, ship and feedback. But all of these are around an order.
With RapidWebDev UI framework, you may only need to implement a order
management page for create, edit and view. And only extend the
implementations with a slight changes for other business processes.
Take
an example, we have an web page to manage products as above screenshot.
Now we need to develop another web page to audit products. The audit
detail panel includes approval decision and comment for a single
product besides all information readonly of the product. In the case,
we can reuse IDynamicPage implementation of product management for
query and create a new implementation to IDetailPanelPage which
inherits from the implementation of product management. Then we can
reuse Load method to load the product detail information.
public class ProductAuditDetailPanel : ProductDetailPanel
{
#region 2 new controls only used in audit detail panel
[Binding]
protected DropDownList DropDownListAuditDecision;
[Binding]
protected TextBox TextBoxAuditComment;
#endregion
public override void LoadWritableEntity(string entityId)
{
Guid productId = new Guid(entityId);
using (ProductManagementDataContext ctx = DataContextFactory.Create<ProductManagementDataContext>())
{
Product p = ctx.Products.FirstOrDefault(product => product.Id == productId);
if (p == null)
throw new ValidationException("The product id doesn't exist.");
}
this.DropDownListAuditDecision.SelectedValue = p.AuditDecision;
this.TextBoxAuditComment.Text = p.AuditComment;
base.LoadReadOnlyEntity(entityId);
}
public override void Update(string entityId)
{
Guid productId = new Guid(entityId);
using (ProductManagementDataContext ctx = DataContextFactory.Create<ProductManagementDataContext>())
{
Product p = ctx.Products.FirstOrDefault(product => product.Id == productId);
if (p == null)
throw new ValidationException("The product id doesn't exist.");
p.AuditDecision = this.DropDownListAuditDecision.SelectedValue;
p.AuditComment = this.TextBoxAuditComment.Text;
ctx.SubmitChanges();
}
}
}
3. Performance Comparison
Problems of ASP.NET and MVC development
If you
have experience on both ASP.NET and MVC web pages, you shall know that
MVC web pages are much faster than a same page written by ASP.NET. The
performance of ASP.NET web form is limited by view state, ASP.NET JS
library and postback. Microsoft provides a dummy AJAX UpdatePanel for
ASP.NET, but it's just develop AJAX-like web forms and doesn't solve
the performance issues really. UpdatePanel posts all form elements to
server asynchrounously by JS and replace the part inner of the
UpdatePanel with returned HTML content. The limitation of ASP.NET is
still there.
Web pages developed by MVC are pretty fast that
there is no large view state and slow ASP.NET JS library. But postback
of MVC follows the original HTTP submit, GET or POST which is not
efficient enough in frequent user interactions. So it's perfect
solution to develop website but not for enterprise business software.
Ideally,
the communication between web forms and server should be JSON data
driven in enterprise software. When an user does any operations in a
web form, the web form only sends JSON data to server asynchronously
and render returned JSON data to the web form by JS. Surely, there are
a ton of JS library for such work, like jquery, ExtJS, YUI and
Microsoft Atlas. But we have to write a lot of JS code in ASP.NET web
forms/MVC view templates which is hard maintainable and poor
productivity. When a software company is under pushing by a contract,
the preferred solution is to sacrifice user experience to improve
productivity.
Solution of RapidWebDev UI framework
RapidWebDev
UI framework creates an balanced approach to develop high performance
AJAX web forms without taking care of JS library by developers. The
user interactions in a web form is abstracted as query, pagination,
sort, create, update, delete and any other custom operations in the
framework. The paragraph below introduces how AJAX works in the
developed web forms by each user interaction.
query
When an user clicks button Query,
the web form only sends the query filters to server asynchronously and
render returned JSON data into grid. The paging and sorting in grid
doesn't the same as query. The web server response size for the
interaction is very small by the size of JSON data only seeing the
screenshot below.
And what developers do is to implement a query method and return a
collection of records as following code snippet. The AJAX request and
rendering is all controlled by the framework.
public override QueryResults Query(QueryParameter parameter)
{
int recordCount = q.Count();
var results = q.Skip(parameter.PageIndex * parameter.PageSize)
.Take(parameter.PageSize).ToList();
return new QueryResults(recordCount, );
}
deleteAfter the user confirms deletion on a
record, the web forms sends a GET request asynchronously to web server
and refreshed grid after get sucessful notification. As the following
code snippet, developers only need to delete the record by the passing
entity id. All the other works is done by the framework, like request,
grid refresh etc.
public override void Delete(string entityId)
{
ConcreteDataObject concreteDataObject = concreteDataApi.GetById(new Guid(entityId));
if (concreteDataObject != null)
{
concreteDataObject.DeleteStatus = DeleteStatus.Deleted;
concreteDataApi.Save(concreteDataObject);
}
}
create, update, view
When an user is
creating, updating or viewing a record in detail panel as following
screenshot, the detail panel acsx template is actually rendered in a
dynamic generated standalone web page but integrated with the main page
by iframework. The detail panel acsx template is wrapped in an ASP.NET
UpdatePanel automatically. When the user "saves", the detail panel
wrapping page postback in dummy AJAX. But the page is only composed of
an ascx template for detail information which is very small, so the
performance is acceptable as well. After the detail panel saved, the
popup modal dialog is closed and the grid is refreshed automatically.
any custom operations
Aggregate panel is used for custom
operations which works in the same way as detail panel. The following
screenshot is when an user clicks button bulk delete, the aggregate ascx template is rendered in a dynamic generated web page in the modal dialog for confirmation.
So, RapidWebDev UI framework provide a innovative approach to develop high performance web forms productively.
4. Maintainability Comparison
With less development complexity and high reusability talked above, the
developed web forms by RapidWebDev UI framework is easy maintainable.
Especially we can easily find the code block to modify because user
behaviors are abstracted and categorized by the architecture of
framework. And if we prefer to developing high efficient AJAX web forms
in ASP.NET or MVC, we have to write a lot of JS code which is very hard
maintainable out of question.
What's RapidWebDev
RapidWebDev Uri: http://www.rapidwebdev.org
RapidWebDev is an infrastructure helps engineers to develop enterprise
software solutions in Microsoft .NET easily and productively. It
consists of an extendable and maintainable web system architecture with
a suite of generic business model, APIs and services as fundamental
functionalities needed in development for almost all business
solutions. So when engineers develop solutions in RapidWebDev, they can
have a lot of reusable and ready things then they can more focus on
business logics implementation. In practice, we can save more than 50%
time on developing a high quality and performance business solution
than traditional ASP.NET development.
Related Topics