Friday, December 23, 2016

Setup SQL Server session state for a web farm

It takes a bit of digging around to get all the information needed to setup out-of-process session state for an ASP.NET web farm. There are a couple of decisions that you need to make and then you need to configure the database and the application. This post explains all this using a real-life project.

The situation

I'm currently working on a project for a large medical center. There is a strong obligation to the public to be always online, especially in case of a large scale emergency.
This leads to interesting choices in infrastructure for their website: everything is redundant and split across multiple locations across the campus.
The database is a SQL Server Availability Group.

Picking the right session state provider

  • In-Process
    Really only suitable for small applications that run in a single server instance.
  • Session-state server
    A TCP service that is hosted on a single server within the infrastructure.
    Since this introduces a single point of failure, it's no good for this project.
  • SQL Server session state
    Stores session state in the database, either persistent or in temporary storage.
    This is a great pick for a web farm but dus incur additional load on your SQL Server installation
  • Redis session state
    This is the new kid on the block for ASP.NET. Since the medical center is a Microsoft shop and has already invested a lot in top-notch SQL performance, this would only incur technical risk and additional costs for infra.

SQL Server session state, but what flavor?

Putting ASP.NET session state is supported very well, there's tooling to set it up for you but before we dive into that there's yet another consideration.
Where to put the session state:

  • Application database
    This would add a couple of tables to the application database and a bunch of stored procedures. It could be a nice fit when hosting at a shared provider and the additional cost of an extra database is not desired.
  • Session state in TempDB
    Session state data is transient by nature so TempDB, which gets cleared on a server restart and will not be replicated to other SQL Server instances seems like a good idea. You can choose to put the TempDB on a different drive from the application db which could help squeeze more IOPS out of your server. Not writing through to the rest of the cluster may also help improve write performance, but this will also cause loss of session state when the cluster needs to fail over, for example due to maintenance.
  • Session state in it's own database
    This mode will store session state in permanent storage and replicate it across the cluster. That's a performance penalty but gives more guarantees for seemles failover when needed.
    The fact that this database is separate from the application allows to easily make different decisions about IT management, for example about backups or even hosting the session state database on a different database instance.
    This is the best match for this project.

Configuring session state

Once we decided on where to store the session state we had to roll out the configuration in our environments. These are the steps to follow:

  1. Setup the session state database using the ASP.NET SQL Server Registration Tool
    %Windows%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regsql.exe -S MyCluster\Prod -U sa -P topsecret -ssadd -ssype p
  2. Configure the connection string in web.config
    I strongly reccommend including the application name in the connection string and keeping the connection time out low.
    <connectionStrings>
    <add name="SessionConnectionString"
    connectionString="Data Source=MyCluster\Prod,1234;user=sa;pwd=topsecret;Connect Timeout=10;Application Name=Kentico;Current Language=English" />
    </connectionStrings>
  3. Setup the machine key in web.config
    If you're running the site on multiple servers or in the cloud, this is a must.
    <system.web>
       <machineKey xdt:Transform="Insert"
         validationKey="..."
         decryptionKey="..."
         validation="SHA1" decryption="AES" />
    </system.web>
  4. Configure session state in web.config
    <system.web>
    <sessionState mode="SQLServer"
    sqlConnectionString="SessionConnectionString"
    compressionEnabled="true"
    cookieless="false"
    timeout="20"/>
    </system.web>

Thursday, October 27, 2016

Kentico + MVC – Fix the split admin site

One of the options you have when using Kentico with MVC is to split the site into an administrative site and a public-facing site.
This is a nice separation of concerns so I would definitely reccommend this, if budget permits of course.
There’s a small snag with this though. By default a Kentico instance will show you the home page, but there is no content in an MVC site; it doesn’t have layouts and master pages defined in the CMS. Somehow that makes the site end up in an infinite redirection loop.

We’ve had to instruct editors to go to the /Admin url manually. This works but it’s an annoyance the customer doesn’t need.

The workaround
The workaround is to install the IIS URL Rewrite module on the server and add the following rule in web.config:

<system.webServer>
  ...
  <rewrite>
      <rules>
          <rule name="Redirect to admin login" stopProcessing="true">
             <match url="^$" />
             <action type="Redirect" redirectType="Permanent" url="/Admin/CMSAdministration.aspx" />
          </rule>
      </rules>
  </rewrite>
</system.webServer>
Now any request to the root of the site will get redirected to the admin home page.

Thursday, October 20, 2016

Kentico DataQuery API – Hydrating a datarow

When working with Kentico’s APIs you sometimes end up with a DataSet. A good example of this is the Search API. For use with transformations in the PortalEngine that’s usually not an issue. MVC however is very much geared towards dealing with strongly typed data. Fortunately you can easily go from a data row to a page or module class using the InfoDataSet class.

Under the hood
At the core of Kentico’s data access implementation is a key architectural descision: all data objects rely on a dictionary internally for the actual values of the properties.
So in the end, all the code you generate from Kentico for custom classes and page types is just a wrapper around that dictionary. You can see this when inspecting the code; the properties don’t have backing fields but instead use the GetValue / SetValue methods to interact with the dictionary.

The nice thing about this architecture is that it makes databinding really easy. There is no need to use reflection to dig up the PropertyInfo, you can call GetValue with the property name.

This is how databinding in XML transforms works. And it’s also the reason that you cannot bind POCO’s in XML transformations directly.

Hydrating datarows
The DataRows in a Table in a DataSet are in effect aldo dictionaries so, given the architecture of Kentico’s data objects, you can probably imagine how easy it is to go from a datarow to string typed Kentico content. To make it even easier there’s the InfoDataSet class. You create the class with a Kentico data object type as the parameter and it can then turn a DataRow into a typed object:

var set = new InfoDataSet();
var typedObject = set.GetNewObject(dataRow);

If you use InfoDataSet like this, it is ofcourse up to you to ensure that the data in the row matches with the data required by the class.

Friday, October 14, 2016

Kentico DataQuery API – Sub-selects

In lots of projects a requirement pops up to count the number of items in a sub set; the number of products in a category for example.

In SQL you’d probably solve this with a sub select or a Common Table Expression (CTE) for more complex scenario’s.

With the Kentico Data API you can do subselects as well.
It’s not well documented so it took me a while to figure it out but here’s how it works:

  CategoryInfoProvider.GetChildCategories(currentCategory.CategoryID, siteId: siteId)
   .Columns("CategoryDisplayName", "CategoryID")
   // "(SELECT COUNT(*) FROM CMS_Category c2 WHERE c2.CategoryParentID=CategoryID)"
   .AddColumn(
      new DataQuery().Source(s => s.SourceExpression = "CMS_Category c2")
      .Columns("Count(*)")
      .Where("CMS_Category.CategoryID = c2.CategoryParentID")
      .AsColumn("ChildCount"))
   .Result;

This particular query will get a DataSet with the category names, ids and the number of child categories. Unfortunately, you do need to use a DataSet here because using a typed result set will not give access to the ChildCount column.

Thursday, October 13, 2016

Kentico DataQuery API

In recent projects we’ve been making heavy use of the Kentico DataQuery API. This API was introduced in Kentico 8 to replace the provider-based data access methods.
It’s sort of like Linq but not quite the same, though you can mix it with Linq to some degree.

In upcoming posts I’ll dive into some of the gritty details and share some of the lessons learned while applying the Kentico DataQuery API in real life projects, both with the classic Portal engine and MVC.

Why should you use this API?

Mostly because Repeaters will only get you so far. As soon things get more complicated you’ll need to fallback to a data access method that gives you more control.
You can write very complex queries in 10 or 20 lines of code with this APU. It’s very much tuned to Kentico and it’s content.

One of the huge benefits of this is that the queries you build in code are actually testable. Yes, you can write integration tests to exercise your code automatically. And yes that actually works.

What are the drawbacks?

No caching. That’s a big one.
All that lovely caching that you get for free with most of the Kentico Portal Engine controls is now your own concern. You do still get to use Kentico’s caching with the handy cache dependencies that we all love to manage on our data intensive web parts. But now you have to write a bit of code to make that work. More on that in another post.

Posts in this series

Further reading:
Introduction to DataQuery
Kentico 8 Technology – DataQuery API