loading...
Cover image for Kentico 12: Design Patterns Part 24 - Improving Our Projects with Configuration

Kentico 12: Design Patterns Part 24 - Improving Our Projects with Configuration

seangwright profile image Sean G. Wright ใƒป8 min read

Kentico 12 - Design Patterns (25 Part Series)

1) Kentico 12: Design Patterns Part 1 - Writing Testable Code 2) Kentico 12: Design Patterns Part 2 - Writing Unit Tests 3 ... 23 3) Kentico 12: Design Patterns Part 3 - Tips and Tricks, Application Structure 4) Kentico 12: Design Patterns Part 4 - Adding Dependency Injection to the CMS 5) Kentico 12: Design Patterns Part 5 - Front-End Dependency Management 6) Kentico 12: Design Patterns Part 6 - Rendering Meta Tags in Kentico 12 MVC 7) Kentico 12: Design Patterns Part 7 - Integrating Web API 2 8) Kentico 12: Design Patterns Part 8 - Setting Up Integration Tests 9) Kentico 12: Design Patterns Part 9 - The Different Ways to Store Content in Kentico 12 MVC 10) Kentico 12: Design Patterns Part 10 - MVC Routing with NodeAliasPath 11) Kentico 12: Design Patterns Part 11 - Unit Testing Custom Page Types 12) Kentico 12: Design Patterns Part 12 - Database Query Caching Patterns 13) Kentico 12: Design Patterns Part 13 - Generating Page URLs 14) Kentico 12: Design Patterns Part 14 - DocumentQuery and ObjectQuery Tips 15) Kentico 12: Design Patterns Part 15 - Output Caching and User Context 16) Kentico 12: Design Patterns Part 16 - Integrating Vue.js with MVC 17) Kentico 12: Design Patterns Part 17 - Centralized Cache Management through Decoration 18) Kentico 12: Design Patterns Part 18 - Preparing for Kentico 2020 19) Kentico 12: Design Patterns Part 19 - Protecting An API Against XSRF 20) Kentico 12: Design Patterns Part 20 - Choosing a Solution Architecture 21) Kentico 12: Design Patterns Part 21 - MVC Widget Tips 22) Kentico 12: Design Patterns Part 22 - Improving Our Projects for Developer Experience 23) Kentico 12: Design Patterns Part 23 - Improving Our Projects with Documentation 24) Kentico 12: Design Patterns Part 24 - Improving Our Projects with Configuration 25) Kentico 12: Design Patterns Part 25 - MVC Page Templates

Laying A Foundation

I've recently had some conversations with other developers about setting up and configuring projects.

I work ๐Ÿ‘ทโ€โ™‚๏ธ for a web development and marketing agency, WiredViews. Working for an agency involves a constant stream of greenfield projects, which means I've had lots of experience "starting" things.

Below I'd like to cover some recommendations for setting up a Kentico 12 MVC project and what benefits these bring.

This is going to be a 3-part post. In this post we're going to look at Documentation.

The previous posts were about Developer Experience and Documentation.

Let's begin!

Cat typing like a champ


Configuration

Configuration represents all the switches and buttons that toggle how our projects work, when we are developing, deploying, and running our apps.

Using HTTPS Locally

Trying to make our local development environments as much like the staging and production servers as possible is something that we developers always strive for.

Docker is great at solving this problem, but classic Full Framework .NET applications aren't the best use-case ๐Ÿ˜ž (unlike .NET Core, which works great with Docker).

Fortunately, there are configuration changes we can make to our Kentico 12 MVC code base to at least enable HTTPS for local development ๐Ÿ‘๐Ÿพ.

First, this can either be accomplished by running the site out of IIS locally or using IIS Express.

I've done both, but my recommendation is to use IIS Express, due to the way it's integrated with Visual Studio.

Now, if we open the .csproj files for both the Content Delivery (MVC) and Content Management (CMS) applications, we can make sure we have all the settings correctly defined.

Inside the first <Project><PropertyGroup> node, we'll want to set 2 properties:

<Project ...>
  <Import ...>
  <PropertyGroup>
    <UseIISExpress>true</UseIISExpress>
    <IISExpressSSLPort>44325</IISExpressSSLPort>

We can set the <IISExpressSSLPort> to whatever we want, as long as its in the 44300 to 44399 range.

We will want different values here for the 2 different web application .csproj files (example: 44325 and 44326).

Next, we need to define some Visual Studio configuration in the <Project><ProjectExtensions> node:

<Project ...>

  <!-- Lots of other stuff -->

  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
        <WebProjectProperties>
          <UseIIS>True</UseIIS>
          <AutoAssignPort>True</AutoAssignPort>
          <DevelopmentServerPort>51872</DevelopmentServerPort>
          <DevelopmentServerVPath>/</DevelopmentServerVPath>
          <IISUrl>https://localhost:44325/</IISUrl>
          <NTLMAuthentication>False</NTLMAuthentication>
          <UseCustomServer>False</UseCustomServer>
          <CustomServerUrl>
          </CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
        </WebProjectProperties>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>

Here we can set the <DevelopmentServerPort> (HTTP) value to whatever we want, typically in the 50000-59999 range, and the <IISUrl> to be https://localhost: plus the port we picked for <IISExpressSSLPort>.

Do this for both the Content Management and Content Delivery applications and open up the solution in Visual Studio.

When we run the apps we should be able to load them over https:// using the ports we defined ๐Ÿ‘๐Ÿพ.

Running https:// locally can be great for catching Mixed Content Warnings early in the development process ๐Ÿ˜€ or testing HSTS.


Using <PackageReference> for NuGet

The Kentico Installation Manager can be used to create new applications or update existing ones.

When creating a new Kentico 12 MVC application, we are given 2 ASP.NET projects - one for content management (the classic CMS application) and one for content delivery (the MVC application).

Both are created using the outdated (but still functional) packages.config style of NuGet package management ๐Ÿ˜‘.

.NET Core uses <PackageReference>, which I've written about in my post Kentico 12 Class Libraries with Modern .NET Core Features.

Read about the benefits of <PackageReference> in Microsoft's documentation ๐Ÿง!

Unfortunately, Visual Studio 2019 no longer supports migrating packages.config to <PackageReference> for ASP.NET projects ๐Ÿ™๐Ÿพโ€โ™€๏ธ.

Fortunatley, we can trick Visual Studio into thinking our project is just another C# class library ๐Ÿ˜ฎ!

If we open up the .csproj for one of our projects, we can see an MSBuild property <ProjectTypeGuids>:

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Using a reference for these identifiers we can see that 349c5851-65df-11da-9384-00065b846f21 represents an ASP.NET project and fae04ec0-301f-11d3-bf4b-00c04f79efbc represents a C# project.

Let's temporarily replace with <ProjectTypeGuids>:

<ProjectTypeGuids>{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Now our project is no longer seen as an ASP.NET project by Visual Studio and we'll see the "Migrate" option when right clicking the packages.config file.

Visual Studio solution explorer right click context menu on the `packages.config` file

We can perform the migration and be one step closer to an ASP.NET Core project - no more needing to manage .NET Framework version for packages or have all the transitive dependencies enumerated our packages list ๐Ÿ’ช๐Ÿฝ!

Once complete we need to make sure to revert the <ProjectTypeGuids> to the original value so that Visual Studio lights up with the correct ASP.NET features we've come to expect for our project.


Web.config transforms for production

When working on our project locally we might have all our configuration defined in the web.config - connection strings, app settings, URL Rewrite rules, debug flags...

But what happens when we deploy our application to another environment where some of these values should change ๐Ÿค”?

Our first approach might be changing the values manually each time we deploy, but this is error prone and requires an additional deployment step (doubly error prone ๐Ÿ˜ฑ!)

A better option is to use the web.config transforms functionality that is built into MSBuild and Visual Studio.

Assume we have a web.config with the following configuration:

<system.web>
    <compilation debug="true" targetFramework="4.7.2"/>

<!-- ... -->

We want the debug="true" locally because we want to hit breakpoints in Visual Studio correctly when running our site.

Then, when we deploy the app to any environment where we are running it outside of Visual Studio, we want to remove that attribute because of the significant performance impact it can have.

In a new ASP.NET project we should be able to right-click the web.config file, in the Solution Explorer in Visual Studio, and see the "Add Config Transform" option.

Click this will add a new file that is a set of instructions to "transform" the web.config for a specific build configuration (eg: Debug vs Release).

Visual Studio solution explorer right click context menu on the `web.config` file

After adding the transform we can see (2) new files - web.Debug.config and web.Release.config:

Visual Studio solution explorer showing the `web.Debug.config` and `web.Release.config` nested under a `web.config` file

Opening up the web.Release.config we can see that Visual Studio has already done us a favor by adding the transform instruction to remove the debug attribute when publishing the app using the Release configuration ๐ŸŽ‰.

 <system.web>
    <compilation xdt:Transform="RemoveAttributes(debug)" />

Now, whether we are publishing to the local filesystem, IIS on a virtual machine, or an Azure App Service, we can be sure our deployed web.config will have the right values for that environment ๐Ÿ‘๐Ÿผ.

If you want to learn more about how to "publish" an ASP.NET application from Visual Studio, check out the official Microsoft documentation.

AppSettings.Local.config

Another way we can take advantage of web.config transforms is with our app settings (and similarly, connection strings).

We don't want to commit sensitive settings into source control, and other team members might want their own local settings ๐Ÿ™„.

We can create local configuration files that are set as the default paths in our web.config and then transform these values to the normal, committed files when publishing.

Here's an example of what the base web.config looks like:

Notice, there's no sensitive values or ones other developers might want to configure here.

<appSettings file="App_Config\AppSettings.Local.config">
    <add key="webpages:Version" value="3.0.0.0"/>
    <add key="webpages:Enabled" value="false"/>
    <add key="ClientValidationEnabled" value="true"/>
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/>
    <add key="aspnet:MaxJsonLength" value="20971520"/>
</appSettings>
<connectionStrings configSource="App_Config\ConnectionStrings.Local.config"/>

The App_Config\AppSettings.Local.config and App_Config\ConnectionStrings.Local.config are both ignored by source control and also not included in the .csproj.

Local config files excluded from the project in Visual Studio solution explorer

They include the sensitive and customizable values we use locally when developing ๐Ÿ˜‰:

<appSettings>
  <add key="CMSHashStringSalt" value="..."/>

  <add key="CMSAzureAccountName" value="..."/>

  <add key="logging:serilog:log-level" value="Debug" />

The web.Release.config then contains a transform instruction to point the source of our settings and connection strings to other files:

<appSettings
    file="App_Config\AppSettings.config"
    xdt:Transform="SetAttributes(file)">
</appSettings>
<connectionStrings 
    configSource="App_Config\ConnectionStrings.config"
    xdt:Transform="SetAttributes(configSource)" />

These two files (App_Config\AppSettings.config and App_Config\ConnectionStrings.config) are both included in the .csproj and source control, but they don't include any sensitive values ๐Ÿคจ.

Visual Studio solution explorer showing 2 sets of settings files

Instead they are either empty (settings could be defined in an Azure App Service directly) or contain tokens that can be replaced by a continuous delivery tool or extension).

URL Rewrite Redirect to CMS Admin

Another cool use of web.config transforms is changing the value of a URL Rewrite rule:

This rule would be added to our Content Delivery application (MVC) web.config.

It says any request for /admin should be redirected to the Content Management application (CMS) which can be found at https://localhost:44326/admin locally ๐Ÿค“.

<system.webServer>
  <rewrite>
    <rules>
      <rule name="Admin-Redirect">
        <match url="^admin"/>
        <action type="Redirect" url="https://localhost:44326/admin"/>
      </rule>
    </rules>
  </rewrite>

<!-- ... -->

However, in production that domain is different. This means in our web.Release.config we have a transform for this URL rewrite rule.

<system.webServer>
  <rewrite>
    <rules>
      <rule xdt:Transform="Replace" xdt:Locator="Match(name)"
            name="Admin-Redirect">
        <match url="^admin"/>
        <action type="Redirect" 
            url="https://admin.your-domain.com/admin"/>
      </rule>
    </rules>
  </rewrite>

<!-- ... -->

I like this solution a lot more ๐Ÿ˜Ž than the one recommended in the Kentico documentation that performs the redirect at the ASP.NET level instead of the IIS level.

Less code, more performant ๐Ÿ’ช๐Ÿฟ!


Conclusion

In this post we looked at the many ways we can improve our Kentico 12 MVC projects by adjusting how they are configured. .csproj settings, NuGet package management, and web.config transforms.

Much of this configuration can be found 'behind the scenes' in an ASP.NET project, which is both good and bad. We don't need to worry about it when getting started (good ๐Ÿ‘!) but we also might not know it's there when we need to customize it (not so good ๐Ÿ‘Ž!).

None of these things are Kentico specific, but if we are new to Kentico or .NET, these suggestions might also be new and helpful.

Hopefully this post pulls back the curtain some and encourages you to explore how you can configure your Kentico 12 MVC project to work better for you and your team.

...

As always, thanks for reading ๐Ÿ™!


Photo by Oscar Nord on Unsplash

We've put together a list over on Kentico's GitHub account of developer resources. Go check it out!

If you are looking for additional Kentico content, checkout the Kentico tag here on DEV:

Or my Kentico blog series:

Kentico 12 - Design Patterns (25 Part Series)

1) Kentico 12: Design Patterns Part 1 - Writing Testable Code 2) Kentico 12: Design Patterns Part 2 - Writing Unit Tests 3 ... 23 3) Kentico 12: Design Patterns Part 3 - Tips and Tricks, Application Structure 4) Kentico 12: Design Patterns Part 4 - Adding Dependency Injection to the CMS 5) Kentico 12: Design Patterns Part 5 - Front-End Dependency Management 6) Kentico 12: Design Patterns Part 6 - Rendering Meta Tags in Kentico 12 MVC 7) Kentico 12: Design Patterns Part 7 - Integrating Web API 2 8) Kentico 12: Design Patterns Part 8 - Setting Up Integration Tests 9) Kentico 12: Design Patterns Part 9 - The Different Ways to Store Content in Kentico 12 MVC 10) Kentico 12: Design Patterns Part 10 - MVC Routing with NodeAliasPath 11) Kentico 12: Design Patterns Part 11 - Unit Testing Custom Page Types 12) Kentico 12: Design Patterns Part 12 - Database Query Caching Patterns 13) Kentico 12: Design Patterns Part 13 - Generating Page URLs 14) Kentico 12: Design Patterns Part 14 - DocumentQuery and ObjectQuery Tips 15) Kentico 12: Design Patterns Part 15 - Output Caching and User Context 16) Kentico 12: Design Patterns Part 16 - Integrating Vue.js with MVC 17) Kentico 12: Design Patterns Part 17 - Centralized Cache Management through Decoration 18) Kentico 12: Design Patterns Part 18 - Preparing for Kentico 2020 19) Kentico 12: Design Patterns Part 19 - Protecting An API Against XSRF 20) Kentico 12: Design Patterns Part 20 - Choosing a Solution Architecture 21) Kentico 12: Design Patterns Part 21 - MVC Widget Tips 22) Kentico 12: Design Patterns Part 22 - Improving Our Projects for Developer Experience 23) Kentico 12: Design Patterns Part 23 - Improving Our Projects with Documentation 24) Kentico 12: Design Patterns Part 24 - Improving Our Projects with Configuration 25) Kentico 12: Design Patterns Part 25 - MVC Page Templates

Posted on Jun 3 by:

seangwright profile

Sean G. Wright

@seangwright

dev lead @WiredViews, founding partner @craftbrewingbiz. @Kentico MVP. love to learn / teach web dev & software engineering, collecting vinyl records, mowing my lawn, craft ๐Ÿบ

Discussion

markdown guide