Friday, January 15, 2010

Need to Avoid ASP.Net web.config Inheritance?

I ran across a situation yesterday where ASP.Net web.config inheritance bit me in a way that wasn’t pleasurable.  Here is my situation.
I have 2 ASP.Net web applications on a single IIS web site, 1 ASP.Net web app and 1 WCF web services.  For example:
IIS Setting Virtual Path Comments
www.MyWepApp.com \MyWebApp This is my 2.0 ASP.Net web application
www.MyWebApp.Com\MyServices\ \MyWebApp\MyServices This is my 3.5 WCF web services

As you can see, I have a hierarchy going on where “MyServices” is an IIS web application under the IIS web site (also an IIS web app) “MyWebApp” and “MyServices” is a sub-directory under “MyWebApp.”  Since we have a web application at the IIS web site root (“MyWebApp”) and that site has a web.config, ASP.Net inheritance kicks in for any web applications that are “children” to “MyWebApp.”
So the error message I was getting was that I already had a <sectionGroup> defined for “System.Web.WebExtensions" when I tried to invoke a web service under “MyServices.”  Sure enough, If I looked at both web.config’s the <configSections> were duplicated.  After some searching I found you can do two things to avoid these conflicts.  (1) Make sure the <confiSections> point to the same assembly versions and (2) add a <location> element to your root web.config and set it to not allow child inheritance.
So let’s look at (1) – Making sure the <configSections> match.  Here is the config section from the web app:
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>

Here is the section from my web services:
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
<section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
<section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>

Notice the differences in the Version numbers of the assemblies.  The Web app had references to the 1.0 assemblies and the web services had references to the 3.5 assemblies.  Because of ASP.Net inheritance, whenever I tried to consume a web service it tried to “re-write” the same config sections because they have the same name but different values.  Updating the <configSection> of the web app to match the web services (all pointing to the 3.5 assemblies allowed me to work.  This is because the <configSections> that have the same name in both web.configs now have the same value.

For (2) adding the <location> element to the root web.config here is the line I added:

</configSections>
<location inheritInChildApplications="false">
<system.web>

There are a couple of things to note here.  (1) the <location> element must be after the <configSections> and before the <system.web>  If you have <appSettings> or any thing else those must also be outside the <location> element.

(2) the “inheritInChildApplications” attribute is not defined in the asp.net web.config schema which is why it has a red underline.  That is ok.  Apparently Microsoft added this to the asp.net engine but not to the schema.  Whatever it works.

(3) because of note (1) and the fact that <location> wraps the <system.web> section, all other sections are using asp.net inheritance, which is why we have to deal with (1) and the same version of the <sectionGroups> assemblies anyhow.

5 comments:

  1. Nice article but would run into problems on a larger system with multiple versions of .net as every application under would the assemblies updating, which is not something I would like to risk with a heavily used, stable application.

    Another possible approach is to use assembly binding. From what I understand this allows you to specify an older assembly in the configSections that matches your root application, which is then overriden later in the web config using the following:














    Like I said, only just started looking into this, but it is one solution.

    ReplyDelete
  2. Thanks for the reply Scott.

    Yeah, I think the binding will work. I remember reading about that before but I have not personally tried it.

    You are right though, all applications would be using the 3.5 version of those assemblies.

    I think this might be ok though. The biggest jump I would be worried about would be from 1.0/1.1 to anything greater. And I'm guessing that those assemblies listed are backward compatible. If you have different apps in 2.0, 3.0 and 3.5 it is ok for them to use the 3.5 assemblies. I believe (I would have to double check to be sure) that 2.0 -> 3.5 only introduced WCF, WF, cardspace and WPF. The framework did not change. From 3.0 -> 3.5 it was only updates to WCF / WF and some slight language enhancments and again the actual framework was not update. They are still using the 2.0 framework. Those versions should have been called 2.1 and 2.2 IMO.

    The game probably changes with the release of 4.0 as it is a new framework.

    But in any case, I think I might research those specific assemblies. I'm really thinking they are backwards compatibly and it should be ok.

    Thanks! -- Ed

    ReplyDelete
  3. Binding just run old ver with new one from 2.0 to 3.5 but but its no that good solution.
    safdar

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete