Web Site: .NET Multi Web Application Development

Aside Posted on Updated on

Multi-web Application Development – Part I

This post describes how to create, setup and configure multiple web applications to appear as one application. It shows how to setup a development environment that uses localhost to works similar to dev, test and production environments. The only difference between the environments is the domain name in the URL. The following table is an example showing the difference in URL’s between different environments used in an organization.

Environment URL
Local https://localhost/apps/app1
Development https://dev/apps/app1
Test https://test/apps/app1
Production https://prod/apps/app1

Why is all this important? Have you ever had multiple versions of an application and needed to debug an older version? Or, have you ever developed an application using IIS Express (F5) only to have it look and work different once it’s deployed to dev?

A common reason for deployment problems developing locally on IIS Express (F5) is because it’s a different web server and physical paths don’t always match to the same relative path as the deployment server’s path, especially if you’re using virtual directories. This usually happens when there are common JavaScript and CSS files used in multiple web applications. I’m not an expert with IIS Express but it seems to use a location that is tied to where the project is located and is not the same as the IIS Web Site physical path.

Here the main question is, is there any way to setup and configure a development environment to provide consistent results when deployed to dev, test, and prod. My answer is yes, if you use IIS and localhost instead of IIS Express.

Advantages to using IIS instead of IIS Express

  1. Modify JavaScript, CSS, cshmtl, and html files without rebuilding the web application,
  2. Unnecessary to close browser each time for code, build, debug cycle during development of web application,
  3. Access web applications in separate browser anytime, even when Visual Studio is closed,
  4. Debug multiple web applications in one instance of Visual Studio

Step 1: Setting up physical directory structure
IIS configuration uses inheritance through the use of its web.config file. Knowing how to take advantage of it is important during development and deployment.

The default directory for IIS is C:\inetput\wwwroot, but it doesn’t have to be that directory. It can point anywhere. I like using the following structure because it allows me to create multiple websites on my development system.

In this example, the website physical path is D:\IISWebsites\localhost\wwwroot.

+- D:\IISWebsites
   +- localhost
      +- AdminScripts
      +- custerr
      +- logs
      +- temp
      +- wwwroot
          +- appName
          |  +- api
          |  +- apps
          |  +- web.config
          +- aspnet_client
          |  +- cdn
          |  |  +- angular
          |  |  +- bootstrap
          |  |  +- SmartAdmin
          |  +- css
          |  +- js
          +- Content

In IIS, the above folders show up under the Default Web Site as shown in the following image. While the folder structure looks the same, we’re putting the applications in a different physical location that separates them from the website.

+- E:\IISApps
   +- appName
      +- api
      |  +- WebApi01
      |     +- bin
      |     +- Areas
      |  +- WebApi02
      |     +- bin
      |     +- Areas
      |  +- WebApi03
      |     +- bin
      |     +- Areas
      |  +- WebApi04
      |     +- bin
      |     +- Areas
      +- apps
         +- WebApp01
            +- bin
            +- Areas
         +- WebApp02
            +- bin
            +- Areas
         +- WebApp03
            +- bin
            +- Areas

In IIS, the left pane looks similar to the following tree. The aspnet_client folder in each application is a virtual directory that points to the physical location defined by the web site, in this case D:\IISWebsites\localhost\wwwroot\aspnet_client. So far, this is they only way I’ve been able to share bundles between the web applications.

+- Default Web Site
   +- aspnet_client
   +- api
   |  +- WebApi01
   |  |	+- aspnet_client
   |  |	+- Views
   |  +- WebApi02
   |  |	+- aspnet_client
   |  |	+- Views
   |  +- WebApi03
   |  |	+- aspnet_client
   |  |	+- Views
   |  +- WebApi04
   |  	+- Views
   +- apps
      +- WebApp01
      |	 +- aspnet_client
      |	 +- Views
      +- WebApp02
      |  +- aspnet_client
      |  +- Views
      +- WebApp03
         +- aspnet_client
	 +- Views

So far, I’ve been able to use these concepts to work with TFS and SVN successfully. It’s helped me with my development of multiple web applications.

Modernizing a Classic ASP Web Application to .NET

Posted on Updated on

I’m currently working on a classic asp system started in the late 90’s. It was developed in ASP using Sybase. The system consists of a classic asp application, Windows PowerBuilder application, Windows PowerBuilder Batch applications, and Java Batch applications. The windows server is 2003 and Sybase is 12.5. The primary reason the database hasn’t been upgraded is because of the PowerBuilder applications. We’re in the process of rewriting the batch applications and the single Windows application by adding the features and capabilities to the web application.

It’s my understanding there have been two attempts to rewrite the system in Java, both attempts failed. Why did they fail? I’m not sure, but I believe it was because it was taking too long and the users were not happy with the UI. Based on my experience, doing a rewrite on a large system rarely succeeds because the tendency is to redo everything.

In the early 90’s there was an initiative at the company I was working to rewrite some DOS applications used by their sales engineers with a development team of three programmers. The initiative called for a new Windows 3.1 system to be developed in PowerBuilder. It was going to take 18 months to completely rewrite and deliver the system, seven years later was the first released. It was called a success, but I think it was management’s spin on something they couldn’t let fail.

Another time, I was asked to estimate a rewrite on a project that was written in Microsoft Access. My basic estimate was that it would take three developers about 3 to 4 years or 10 man years. Executive leadership went with a consulting firm because the effort was going to take too long and the consulting company could do it cheaper and faster. In the end, it took two full time developers (on-site) around six years to deliver the first release.

I’m not saying you can’t do a complete rewrite of a large legacy system, what I’m saying is that stopping everything and doing a complete rewrite is usually destined to fail. It’s no fault of anyone involved in the project, it’s a matter of expectations and what it takes to rewrite a large system. The time and resources required to redesign and rewrite a legacy system is significant and usually the task is grossly underestimated because management insists it can’t take that long, it’s only code.

Given that small rant, I believe any organization wanting to modernize their system needs a good plan in place and needs to look at how they can achieve modernizing a system without rewriting the system all at once. One thing I’ve noticed over the years is doing development in small chunks and delivering often (3 or 4 times a year) seems to work the best. Management likes it and users accept small changes easier than having everything change at once.

At my current place of employment, I believe we’re on a good track for rewriting the current asp system because we’re not trying to do everything at once. It’s going to take time and project management is trying to lay out the order of everything, but we’re doing small pieces for each release which allows us to make adjustments as technology and requirements change. We made the GUI look and work the same as the legacy system to make the rewrite as transparent as possible to the end user. So far, things are working out very well.

The basic architecture for the new system is to create restful web services for backend processing and use .NET MVC 5 with AngularJS for client side pages. It’s not complex and Microsoft has made it easy to create restful web services, which is helping the process. The biggest problems we have are related to Sybase and how Sybase works with the ORM (Dapper). As a whole Dapper works really well, except it does have issues with Sybase which (based on reading the internet) do not seem to be happening with SQL Server or Oracle.

How does the architecture look in the new system? In order to keep things short I’ll write about that in my next post, which could take a while.

Why Good Employees Leave Their Jobs

Posted on Updated on

Great article on why good employees leave their job.

8 Bad Mistakes That Make Good Employees Leave – Forbes

Learning AngularJS

Posted on

Just finished a class on AngularJS. The title of the class was Developing Web Applications Using AngularJS. It was a four day class and the instructor was Brad Gillespie who was a very good instructor. Overall, I have to admit it was a pretty good class that provides an overview on the basics of using angularJS on the client side for web applications, which fit in perfectly for where we’re at with regards to our system.

We are currently in the process of remodeling our legacy Classic ASP web application into several .NET web applications. We’re using .NET WebApi 2.x for restful services and .NET/MVC 5 and angularJS for client side applications.

For the restful web services we’re breaking those into several applications by function, which at the moment is by main menu classification. We’ve created three client side applications, one for admin, education institutions, and educators.

For the end user, there is little difference in the appearance of the system. The menus, page headers and footers are the same as the original application. As we replace existing pages we’ll be able to remove them from the legacy application.

I estimate the remodeling or rewrite will take about seven years to complete when we consider enhancements, bug fixes and changes we’ll be required to make because of changes in state and federal laws.

This is a picture of the certificate that says I completed the class.

Certificate of Completion

Forget Technical Debt — Here’s How To Build Technical Wealth

Posted on Updated on

Great article published in People & Culture Magazine, found this article on Flipboard August 11, 2016. Forget Technical Debt — Here’s How To Build Technical Wealth

How does one approach legacy code? Andrea Goulet and business partner Scott Ford love legacy code. Andrea and her team believe people are more than just makers, they are menders too!

Here’s 3 paragraphs from the article I think describe most of the development efforts I’ve been involved in over the years.

Sure, it might not make sense to test compulsively when you’re pushing toward a prototype. But once you have a product and users, you need to start investing in maintenance and incremental improvements. “Too many people say, ‘Don’t worry about the maintenance, the cool things are the features!’” says Goulet. “If you do this, you’re guaranteed to hit a point where you cannot scale. You cannot compete.”

As it turns out, the second law of thermodynamics applies to code too: You’ll always be hurtling toward entropy. You need to constantly battle the chaos of technical debt. And legacy code is simply one type of debt you’ll accrue over time.

“Again the house metaphor applies. You have to keep putting away dishes, vacuuming, taking out the trash,” she says. “if you don’t, it’s going to get harder, until eventually you have to call in the HazMat team.”

I see this a lot. Start on a new system and the initial focus is to get it working and usable and no one is really concerned with the structure of the system or how the code looks as long as it works. Once the initial system is working, work on fixing bugs and implementing enhancements. The end result is, we never go back to refactor the code base, especially the parts that could be promoted into base classes and generalized to help move towards following the basic principals defined in the SOLID acronym.

It’s an easy trap to fall into because management is focused on moving forward with new development and as long as the system works, why go back and take time to improve the code base that no one sees but the developers.

Whenever we started a new application, I had one director who would always say, “every project has a start and a finish”. Once it’s finished the application goes into maintenance mode not to be touched again unless absolutely necessary. Once an application was delivered to production we spent the next several months fixing bugs, after that the system wasn’t looked at again.

One of the short comings of that thought process was the lack of understaning how everything worked or fit together. There wasn’t time to create technical wealth. The director couldn’t understand how working on one application could benefit all applications in the system. They were missing the point about software development and not looking at the big picture, which makes is worth quoting the following statement from the article.

Stop thinking about your software as a project. Start thinking about it as a house you will live in for a long time.

I know it’s hard to go back and refactor code once because it seems like you’re spending too much time on one area of code. If the system is around long enough there are parts that will need to be revisited several times to keep it going and making it better so you won’t need to rewrite it from scratch in five or six years.

It’s like a house, when the foundation and the frame are solid and flexible, it has a better chance to survive anything bad happening around it.

The Quiet Crisis unfolding in Software Development

Posted on Updated on

Great post by Bill Jordan on May 27, 2016. He made a lot of good points about software developers and the state of software development.

The Quiet Crisis unfolding in Software Development

Share Bundles in ASP.NET Applications

Posted on Updated on

I’m working on a system that needs to work and look as one web application. They share the same css styles and several JavaScript files. In an MVC application, Microsoft provides the ability to bundle styles and javascript, but everything I’ve seen and read so far puts the stylesheets and javascripts files in the applications /Content or /Scripts folder. In the past I used the aspnet_client folder to share common stylesheets and javascript files and it was not a problem because I was doing Web Forms and not using the bundling capabilities in .NET.

The current project is three .NET MVC 5 applications and they use the same stylesheets and share some custom javascript files, not to mention Angular, Bootstrap, and so on. For Angular and Bootstrap I can use the CDN but I’d like to really just bundle everything into a couple of bundles. I did figure out a way to share everything and store the files it the aspnet_client folder, but came across a few problems when I started to do the builds in Release mode.

The default location for the IIS website is C:\inetpub\wwwwroot and aspnet_client directory is there as a place where usually third party libraries will install their software. The aspnet_client directory is a global location for all web applications in the website. I think this is a good place to put common css and javascript files, even the ones from angular, bootstrap and so on if you don’t want to use the CDN for those third party packages.

To make shared bundling work, each application needs to create a virtual directory to point to the aspnet_client physical location. For three applications, let’s call them app1, app2, and app3. We need to create a virtual directory called aspnet_client in each of the applications.

This is one configuration that works:

C:\inetpub\wwwroot +-- aspnet_client | +-- | +-- angular | | +-- 1.5.5 | +-- angular.uigrid | +-- 3.1.0.1 | +-- bootstrap | | +-- 3.3.1 | | +-- 3.3.6 | +-- custom | | +-- 1.0.0 | +-- custom | | +-- 1.0.0 | +-- jquery | | +-- 2.1.1 | +-- jquery.validation | +-- 1.14.0 +-- Dahl (this is the primary path for a family of applications in the website) +-- api +-- apps

On the D: drive I put each of the applications in their own folder. The aspnet_client below is a virtual directory in the application. The physical directory doesn’t exist on the D: drive, but instead points to c:\inetpub\wwwroot\aspnet_client.

D:\ +-- Dahl +-- api | +-- api1 (web api application #1) | +-- api2 (web api application #2) | +-- api3 (web api application #2) +-- apps +-- app1 (mvc application #1) | +-- aspnet_client | (physical path is c:\inetpub\wwwroot\aspnet_client) +-- app2 (mvc application #2) | +-- aspnet_client | (physical path is c:\inetpub\wwwroot\aspnet_client) +-- app3 (mvc application #3) +-- aspnet_client (physical path is c:\inetpub\wwwroot\aspnet_client)

The default Bundling in .NET doesn’t allow you to use an absolute path. The path needs to be relative to the application it’s being used in, so /aspnet_client/… doesn’t work, you get an error. Using ~/aspnet_client works, even if aspnet_client is a virtual directory in the application. This is just one example, but there is a lot of different ways to configure this and make it work. In the RegisterBundles, I coded it as follows. Notice the bundle path mirrors the virtual path from aspnet_client/… to the last part in the bundle name.

I’ve read on stackoverflow where some say it’s a hack because the virtual path needs to mimic the actual path in the application and they shouldn’t have to do it this way. Technically they’re correct, but we have to live with it until Microsoft decides to fix it. For angular, bootstrap, and jquery files, using the CDN option is probably more appropriate but this shows how to do it without using a CDN.

public static void RegisterBundles(BundleCollection bundles)
{
    bundles.Add( new ScriptBundle( "~/aspnet_client/jquery/js" ).Include( 
                                   "~/aspnet_client/jquery/2.1.1/jquery.js",
                                   "~/aspnet_client/jquery.validation/1.14.0/jquery.validate.js" ) );

    bundles.Add( new ScriptBundle( "~/aspnet_client/angular/js" ).Include(
                                   "~/aspnet_client/angular/1.5.5/angular.js",
                                   "~/aspnet_client/angular/1.5.5/angular-animate.js",
                                   "~/aspnet_client/angular/1.5.5/angular-route.js",
                                   "~/aspnet_client/angular/1.5.5/angular-touch.js",
                                   "~/aspnet_client/angular/1.5.5/angular-mocks.js" ) );

   bundles.Add( new ScriptBundle( "~/aspnet_client/angular.ui-grid/js" ).Include( 
                                  "~/aspnet_client/angular.ui-grid/3.1.0.1/Scripts/ui-grid.js",
                                  "~/aspnet_client/angular.ui-grid/3.1.0.1/grunt-scripts/csv.js",
                                  "~/aspnet_client/angular.ui-grid/3.1.0.1/grunt-scripts/pdfmake.js",
                                  "~/aspnet_client/angular.ui-grid/3.1.0.1/grunt-scripts/vfs_fonts.js" ) );

    // Use the development version of Modernizr to develop with and learn from. Then, when you're
    // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
    bundles.Add( new ScriptBundle( "~/aspnet_client/js/js" ).Include( 
                                   "~/aspnet_client/js/respond/1.2.0/respond.js",
                                   "~/aspnet_client/js/modernizr/2.6.2/modernizr-2.6.2.js" ) );

    //-------------------------------------------------------------------------------------
    //- Styles section
    bundles.Add( new StyleBundle( "~/aspnet_client/bootStrap/css" ).Include( 
                                  "~/aspnet_client/bootStrap/3.3.6/respond.js",
                                  "~/aspnet_client/bootStrap/3.3.6/bootstrap.css",
                                  "~/aspnet_client/bootStrap/3.3.6/bootstrap-theme.css" ) );

    bundles.Add( new StyleBundle( "~/aspnet_client/Custom/css" ).Include( 
                                  "~/aspnet_client/Custom/1.0.0/Custom.css",
                                  "~/aspnet_client/Custom/1.0.0/CustomLeftNav.css",
                                  "~/aspnet_client/Custom/1.0.0/dashboard.css" ) );

    bundles.Add( new StyleBundle( "~/aspnet_client/angular.ui-grid/3.1.0.1/Content/css" ).Include( 
                                  "~/aspnet_client/angular.ui-grid/3.1.0.1/Content/ui-grid.css" ) );

In the cshtml master layout file I used the following, content removed to keep it shorter.

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    @Styles.Render( "~/aspnet_client/bootStrap/css" )
    @Styles.Render( "~/aspnet_client/Custom/css" )
    @RenderSection( "styles", required: false )
</head>
<body>
    <div>
        @RenderBody()
    </div>

@Scripts.Render( "~/aspnet_client/jquery/js" )
@Scripts.Render( "~/aspnet_client/angular/js" )
@Scripts.Render( "~/aspnet_client/js/js" )
@RenderSection( "scripts", required: false )
</body>

In one of the view pages where the angular grid is used, include the following.

@section styles {
@Styles.Render("~/aspnet_client/angular.ui-grid/3.1.0.1/Content/css")

<div>
   page content...
</div>

@sections scripts {
@Scripts.Render("~/aspnet_client/angular.ui-grid/js")
}