0

I have a website with multiple host names bound to it. Is it possible to find out in Application_Start() which host is being used?

I understand that I don't have a request at this point so I'm thinking probably not, however I know that each host through which the application is accessed will run under a new app pool. So just wondering if there's anything I can query in IIS that might tell me what hostname is being used with the current app pool?

BVernon
  • 3,205
  • 5
  • 28
  • 64
  • What are you trying to accomplish? – John Saunders Mar 24 '15 at 19:59
  • @JohnSaunders I've got one application in terms of a code base, but I have multiple customers who use this application under different host names. When a request comes in I get configuration info for them. This isn't a big deal since it's put into a static variable and if it's set I don't set it again. But I was just curious if there was a way to just do it when the application starts since theoretically I should have all the information I need to do so. – BVernon Mar 24 '15 at 20:06

2 Answers2

2

Well, IIS isn't the domain authority, so it will only work to read IIS settings if you manually keep your local settings and DNS settings in sync. But there is a way to read the IIS bindings. So, if you use host header names in your IIS configuration you can effectively read the domain name.

// Get the current Site Name
var siteName = HostingEnvironment.SiteName;

// Get the sites section from the AppPool.config
var sitesSection = WebConfigurationManager.GetSection(null, null, "system.applicationHost/sites");

var site = sitesSection.GetCollection().Where(x => string.Equals((string)x["name"], siteName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

if (site != null)
{
    foreach (var iisBinding in site.GetCollection("bindings"))
    {
        var protocol = iisBinding["protocol"] as string;
        var bindingInfo = iisBinding["bindingInformation"] as string;

        string[] parts = bindingInfo.Split(':');
        if (parts.Length == 3)
        {
            //string ip = parts[0]; // May be "*" or the actual IP
            //string port = parts[1]; // Always a port number (even if default port)
            string hostHeader = parts[2]; // May be a host header or "". This will only be the domain name if you keep it in sync with the DNS.

            // Use the hostHeader as the domain name
        }
    }
}

For this to work, you need to set a reference to Microsoft.Web.Administration.dll, which is available via NuGet.

Reference: http://blogs.msdn.com/b/carlosag/archive/2011/01/21/get-iis-bindings-at-runtime-without-being-an-administrator.aspx

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • 1
    Ok, so this gets me the list of bindings... but how do I know which one the calling code is associated with? – BVernon Mar 24 '15 at 20:24
  • 1
    Good point. I was assuming that you were hosting each application as a different IIS site, so locating it would just be a matter of finding the correct binding string with StartsWith or a RegEx. But if you are hosting all of the domains in the same site this won't work. I have since come up with a better solution to your problem and added another answer, but this one still could be helpful to somebody. – NightOwl888 Mar 25 '15 at 12:40
2

Is it possible to find out in Application_Start() which host is being used?

AFAIK no, it is not.

Based on your comments, I would say the persistence mechanism you are after is one of the server-side caching options (System.Runtime.Caching or System.Web.Caching).

System.Runtime.Caching is the newer of the 2 technologies and provides the an abstract ObjectCache type that could potentially be extended to be file-based. Alternatively, there is a built-in MemoryCache type.

Unlike using a static method, caches will persist state for all users (and all domains) based on a timeout (either fixed or rolling), and can potentially have cache dependencies that will cause the cache to be immediately invalidated. The general idea is to reload the data from a store (file or database) after the cache expires. The cache protects the store from being hit by every request - the store is only hit after the timeout is reached or the cache is otherwise invalidated.

You can populate the cache the first time it is accessed (presumably after the request has already been populated, not in the Application_Start event), and use the domain name as part of (or all of) the cache key that is used to look up the data.

public DataType GetData(string domainName)
{
    // Use the domain name as the key (or part of the key)
    var key = domainName;

    // Retrieve the data from the cache (System.Web.Caching shown)
    DataType data = HttpContext.Current.Cache[key];
    if (data == null)
    {
        // If the cached item is missing, retrieve it from the source
        data = GetDataFromDataSource();

        // Populate the cache, so the next request will use cached data

        // Note that the 3rd parameter can be used to specify a 
        // dependency on a file or database table so if it is updated, 
        // the cache is invalidated
        HttpContext.Current.Cache.Insert(
            key, 
            data, 
            null, 
            System.Web.Caching.Cache.NoAbsoluteExpiration, 
            TimeSpan.FromMinutes(10), 
            System.Web.Caching.CacheItemPriority.NotRemovable);
    }

    return data;
}

// Usage
var data = GetData(HttpContext.Current.Request.Url.DnsSafeHost);

If it is important, you can use a locking strategy similar to Micro Caching in ASP.NET (or just use that solution wholesale) so the data source does not receive more than one request when the cache expires.

In addition, you can specify that items are "Not Removable", which will make them survive when an application pool is restarted.

More info: http://bartwullems.blogspot.com/2011/02/caching-in-net-4.html

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Thanks, that's basically what I'm doing. I have a base page that gets the data on the first request but not on subsequent requests. Except the data I'm caching will change so infrequently (if ever) that it's not even worth ever invalidating so that's why I just use a static variable. – BVernon Mar 25 '15 at 14:48