EWL versus React/ASP.NET: A code comparison

In a previous post I stated that the EWL implementation of RealWorld contains 60-80% less code than its React + ASP.NET Core counterpart. Now I’ll show you why.

As developers, let us imagine implementing a new feature in RealWorld. On the article page, we’ll add a Flag as Spam button for readers. We’ll show the button to everyone except the author. And we’ll back this feature in the database with an IsSpam boolean column in the Articles table.

Step 1: Add the database column

In ASP.NET Core

We first add a new property to our Article entity class:

public bool IsSpam { get; set; }

(View full diff)

Entity Framework Core (our data-access layer) will see this property when creating the database and include it as a column in the Articles table.

Missing piece: The project lacks database-migration infrastructure, meaning that we cannot add the IsSpam column to an existing database or specify a value (i.e. true or false) for existing article rows. Entity Framework Core does support migrations but they are not configured in the project.

We also add a line in the create-article request handler to set the column value for new articles:

IsSpam = false

(View full diff)

In EWL

First, we append these lines to Database Updates.sql:

alter table Articles add IsSpam bit null /* add nullable column */
update Articles set IsSpam = 0 /* set value for existing articles */
alter table Articles alter column IsSpam bit not null /* make column non-nullable */

(View full diff)

We then run Update-DependentLogic in the Package Manager Console, which will detect these new lines in the script and execute them. Finally we add a line to the Editor page to set the value for every new article:

mod.IsSpam = false;

(View full diff)

This procedure isn’t much different from its ASP.NET Core counterpart above, aside from being more complete in terms of database migration. But keep reading to see some real divergence.

Step 2: Modify the web API

In ASP.NET Core

Our main task here is to add a new request handler that flags an article as spam:

public class MarkSpam {
    public class Command : IRequest {
        public Command(string slug) {
            Slug = slug;
        }

        public string Slug { get; set; }
    }

    public class CommandValidator : AbstractValidator<Command> {
        public CommandValidator() {
            RuleFor(x => x.Slug).NotNull().NotEmpty();
        }
    }

    public class QueryHandler : IRequestHandler<Command> {
        private readonly ConduitContext _context;

        public QueryHandler(ConduitContext context) {
            _context = context;
        }

        public async Task<Unit> Handle(Command message, CancellationToken cancellationToken) {
            var article = await _context.Articles.FirstOrDefaultAsync(x => x.Slug == message.Slug, cancellationToken);

            if (article == null) {
                throw new RestException(HttpStatusCode.NotFound, new { Article = Constants.NOT_FOUND });
            }

            article.IsSpam = true;
            await _context.SaveChangesAsync(cancellationToken);
            return Unit.Value;
        }
    }
}

(View full diff)

We also need to hook into this handler from a new method in ArticlesController:

[HttpPatch("{slug}")]
[Authorize(AuthenticationSchemes = JwtIssuerOptions.Schemes)]
public async Task MarkSpam(string slug) {
    await _mediator.Send(new MarkSpam.Command(slug));
}

(View full diff)

This causes HTTP PATCH requests on an article to flag it as spam.

Security issue: This web API lacks any kind of user authorization (!), so I won’t include that piece of the feature. This type of mistake is not possible with EWL since the authorization is coupled to the visibility of the button in the UI (see step 3).

In EWL

Grab a coffee and relax: there is no Web API! See my essay about this.

Step 3: Create the button

In React

Before working on the button, we need to make the current user available to the component that will contain the button. This is important because we aren’t going to make the button available to the author of the article. In the article-index component, we forward the user object down to ArticleMeta:

<ArticleMeta currentUser={this.props.currentUser} />

(View full diff)

And in ArticleMeta, we forward it again, down to ArticleActions:

<ArticleActions currentUser={props.currentUser} />

(View full diff)

Also, the button will need to trigger the HTTP PATCH request that we implemented in the web API. We add it to the list of article requests:

spam: slug => requests.patch(`/articles/${slug}`)

(View full diff)

The button needs to update client-side state too. We add a new action type:

export const SPAM_ARTICLE = 'SPAM_ARTICLE';

(View full diff)

And then, in the article reducer, import the action type:

import { SPAM_ARTICLE } from '../constants/actionTypes';

And implement the action:

case SPAM_ARTICLE:
  return {
    ...state,
    article: { ...state.article, isSpam: true }
  };

(View full diff)

Now we’re finally ready to implement the button in the ArticleActions component by importing the action type:

import { SPAM_ARTICLE } from '../../constants/actionTypes';

And adding a dispatch function to props:

onClickSpam: payload => dispatch({ type: SPAM_ARTICLE, payload })

And adding a spam function within the component:

const spam = () => {
  props.onClickSpam(agent.Articles.spam(article.slug))
};

And adding the JSX for the button:

if (!article.isSpam && props.currentUser !== null) {
  return (
    <span>
      <button className="btn btn-outline-danger btn-sm" onClick={spam}>
        <i className="ion-trash-a"></i> Flag Spam
      </button>
    </span>
  );
}

(View full diff)

And now, after ten distinct changes, we have a working button in React.

In EWL

We add the button to the article page, as a page action:

.Concat( !info.Article.IsSpam && AppTools.User != null
    ? new ButtonSetup( "Flag as Spam", behavior: new PostBackBehavior( postBack: PostBack.CreateFull(
        id: "spam" /* not a magic string; just needs to be unique on page */,
        firstModificationMethod: () => {
            var mod = info.Article.ToModification();
            mod.IsSpam = true;
            mod.Execute();
        } ) ) ).ToCollection()
    : Enumerable.Empty<ActionComponentSetup>() )

(View full diff)

And that’s it. The button’s action (flagging the article as spam) can only execute when the button is present on the page, so there is no need to have a separate authorization check on the action. Remember that this is server-side code and cannot be bypassed.

This is why abstraction matters

You can now see why we call EWL a “new level of abstraction for enterprise web applications.” If you’re developing a line-of-business application with front-end JavaScript, I hope you have a good reason.

If you like what you just read, subscribe to our mailing list!


Thanks to Don McNamara and Jonathan McKenzie for help with React.

The smallest RealWorld implementation

Five years ago, I wrote an essay titled SPAs are Just Harder, and Always Will Be. I showed why traditional, server-side web apps are easier to build and maintain than Single-Page Applications (SPAs). The EWL team has applied this concept and, with the help of our built-in user interface, we’ve just completed the smallest RealWorld example implementation to date.

EWL RealWorld is available on GitHub. The following two charts show how it compares to the combination of the React front end with the ASP.NET Core back end.

.NET code measured by NDepend, with generated code excluded.

.NET code measured by NDepend, with generated code excluded.

React JavaScript/HTML code measured by Jacek Schae&nbsp;with cloc. React CSS code measured from Conduit template, which is provided by RealWorld but is something you would maintain if you owned the app.

React JavaScript/HTML code measured by Jacek Schae with cloc. React CSS code measured from Conduit template, which is provided by RealWorld but is something you would maintain if you owned the app.

What Makes the Difference?

The EWL implementation is not only smaller because of its traditional server-side architecture, but also because EWL includes a built-in user interface and automatic form layout much like what is provided by proprietary low-code platforms such as Salesforce and Mendix. There is no hand-coded HTML and very little CSS. As an EWL developer, you give up some control over your app’s look and feel in exchange for a big productivity boost.

Other Benefits

EWL RealWorld gets a lot for free:

It also runs on an open, cloud-agnostic operations platform.

Short-Term Weaknesses

We began the EWL project with the birth of .NET in 2002. Its web framework started as an extension of ASP.NET Web Forms, which is why EWL systems still include .aspx files and ugly, query-string-parameter URLs. We’re in the midst of a multi-year project to move the web framework to ASP.NET Core, which will fix these issues while also enabling all EWL systems to run on Linux. The bulk of our changes are taking place under the hood, which means you can begin an EWL project today and expect no more than the usual number of breaking changes each month (the home page explains our continuous-adaptive-maintenance approach).

Spring 2018 new web framework features

EWL’s web framework now includes some new, .NET-Core-compatible (i.e. without Web Forms dependencies) form controls: TextControl, EmailAddressControl, and TelephoneNumberControl. Similar to the new HTML editors and hidden-field component, these take most of their parameters via Setup objects to avoid duplicate parameters in (1) the form-item-getter methods that EWL generates for database columns and page query parameters, and (2) the new DataValue extension methods, which make it easier to create form controls for ephemeral data within a page, such as a search box. The new form controls follow accessibility best practices, for example by supporting proper labeling via the HTML label element and using correct input modes on mobile devices.

We’ve also reimplemented page “autofocus”, which lets you specify the location of keyboard focus when a page loads in the browser. The old implementation depended upon Web-Forms Control IDs, and had a couple of significant design problems that we observed over the years. First, to specify focus on a control, the page needed a reference to the control’s ID. This worked well for simple pages, but in complex pages with many nested components, it took a lot of ceremony to get a control reference all the way up the call stack to the page level. Second, the only way to override the focused control after a post back was to specify the control’s ID before the post back. This is impossible if the control doesn't exist yet! Our new design is based on wrapper components, called autofocus regions, and fixes these flaws. Read more in the release notes.

Autofocus regions

Autofocus regions

Recent EWL Improvements

EWL is constantly under development, with updates being pushed to the Canonical branch almost weekly. Here's some changes recently released.

Application-wide database caching

EWL supports almost-automatic database caching. When this feature is enabled (which is why it's not completly automatic), TableRetrieval queries use row versions to determine if up-to-date rows are already in the cache and if so, does not require pulling data from the database. This ia a major improvement to the existing automatic database caching which was per-request only. This feature is supported for both Oracle and Sql Server databases.

Excel exporting is now supported for free

Our previous implementation for creating Excel files required an Aspose license. We have since replaced the solution with a free solution based on Open XML, at the expense of dropping support for legacy Microsoft Office formats.

Automatic ETag generation for blob responses

When sending a file to the browser, the framework will automatically generate ETags headers. This of course provides the massive benefit of not re-transferring files which a client has already received, benefitting the client, the server and our bandwidth costs!

Now supporting charts

This feature is a little less-new but it still counts. EWL supports both bar and line charts while automatically displaying values in a table and allowing users to export the data into a tabular file. Drawing report pages has never been easier.

Follow @EwlTeam on Twitter to keep up with EWL updates!

My name is Sam and this is why I use EWL

Hello! I'm Sam and I'm a .NET web application developer. I have been contributing to Enterprise Web Library for about six years. I wanted to do our first post on why I use EWL.

I enjoy learning about all of the frameworks that I can, trying to figure out their positive and negative attributes. This includes frameworks that aren't even on the C#/.NET stack, such as PHP, Ruby and Python. I definitely try to keep an open mind.

That being said, EWL is still my first choice when I develop data management systems.

I honestly don't know of a faster way to create a powerful, usable interface for modifying the properties and relationships of entities in a database.

In one sitting I can create, from scratch, a web application that can perform all of the CRUD operations I need for a small to medium sized database. I'm talking about add/modify/delete rows for tables of string/numeric/date columns and their relationships. Inlcuding controls that make sense for the data type. I'm not typing date-strings that need to be parsable by .NET. I'm talking about datepickers and dropdowns you can type in to filter.

It's free

And MIT licensed.

Fast, non-magical data layer.

There are powerful data layers out there. But one problem I find with them is too much magic. What is it actually doing under the hood? Because sometimes that's important. Entity Framework uses this magic that allows you to use normal C# convert code and it converts it to SQL. That's awesome. What's not awesome is that sometimes at runtime you'll find out that some particular C# you tried to use does not translate to SQL. "There is no conversion for myString.ToUpper()". Mistakes like this aren't possible with EWL because it makes an effort to force errors to be found at compile-time.

Looks good enough

I don't need it to look like a work of art, I need it to look professional with minimal effort, but allow me to make the changes I need when I need to make them. EWL is not the framework I use to build a custom, gorgeous portfolio website that is provided to me with mockups from a designer. It's what I use for business applications and the administrative backend to my websites.

So there you have it! If you have time to explore EWL as a possible solution for your future projects, be sure to use the Wiki and the enterprise-web-library tag on StackOverflow.com if you get stuck.