Background – on quirks and compatibility
The .NET Framework (including ASP.NET) strives to maintain near-100% compatibility when an existing framework is updated on a machine. We try to ensure as much as possible that if an application was developed and deployed against .NET Framework 4, it will just continue to work on 4.5. This normally means keeping quirky, buggy, or undesirable behaviors in the product between versions, as fixing them may negatively affect applications which were relying on those behaviors.
Consider creating a new Console Application project targeting .NET Framework 4 and putting the following in Program.cs:
class Program{static void Main(string[] args) {List<int> list = new List<int>() { 1, 2, 3 }; list.ForEach(i => {Console.WriteLine(i);if (i < 3) { list.Add(i + 1); } }); } }
When run, the output of this application is 1, 2, 3, 2, 3, 3. Now change the project to target .NET Framework 4.5, recompile, and run again. The output is 1, followed by an exception: System.InvalidOperationException: Collection was modified; enumeration operation may not execute. What happened?
The contract of IEnumerator.MoveNext() specifies that the method shall throw an InvalidOperationException if the underlying collection changes while an enumeration is currently taking place. Indeed, if you use the foreach keyword instead of the List
Given that the .NET Framework 4.5 is an in-place update to .NET Framework 4, how is this pulled off? If you look closely at the console application project, you'll see that it also contains one additional file App.config:
<configuration><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>startup>configuration>
When the application is targeted to 4.5, Visual Studio modifies the configuration file. There are two related concepts in the
<configuration><startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>startup>configuration>
This information is captured into an attribute and compiled into the executable:
[assembly: TargetFramework(".NETFramework,Version=v4.5", FrameworkDisplayName = ".NET Framework 4.5")]
When a component in the .NET Framework needs to decide whether to apply quirks behavior to a particular code path or whether it should use new 4.5-standards logic, it consults the [TargetFramework] attribute to see what framework version the application targets, hence what logic it expects to take place. (If the attribute is not present, the runtime assumes 4.0 quirks behavior. There is additional fallback and resolution logic, but it's not important to the topic at hand.)
But what if ASP.NET applications want to opt in to the new 4.5 behaviors? ASP.NET developers can't use the
To this effect, we introduced the targetFramework attribute on the
<httpRuntime targetFramework="4.5" />
The effect of this attribute is twofold. First, it controls the CLR's quirks mode behavior, just like the
<%@ WebHandler Language="C#" Class="MyHandler" %>using System.Collections.Generic;using System.Web;public class MyHandler : IHttpHandler{public void ProcessRequest(HttpContext context) { context.Response.ContentType = "text/plain";List<int> list = new List<int>() { 1, 2, 3 }; list.ForEach(i => { context.Response.Write(i + " ");if (i < 3) { list.Add(i + 1); } }); }public bool IsReusable {get { return false; } } }
If the targetFramework attribute reads "4.0", the output will be 1 2 3 2 3 3. If the targetFramework attribute reads "4.5", an InvalidOperationException will be thrown due to the collection having been modified during the ForEach() operation.
Second,
<configuration><appSettings><add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" /><add key="ValidationSettings:UnobtrusiveValidationMode" value="WebForms" />appSettings><system.web><compilation targetFramework="4.5" /><machineKey compatibilityMode="Framework45" /><pages controlRenderingCompatibilityVersion="4.0" />system.web>configuration>
By inferring all of these settings from a single line, we help shrink the size of Web.config. Thus the file contains less runtime configuration and more application-specific configuration, such as connection strings.
I'll go over each of these in turn, explaining what they do.
Enables the new await-friendly asynchronous pipeline that was introduced in 4.5. Many of our synchronization primitives in earlier versions of ASP.NET had bad behaviors, such as taking locks on public objects or violating API contracts. In fact, ASP.NET 4's implementation of SynchronizationContext.Post is a blocking synchronous call! The new asynchronous pipeline strives to be more efficient while also following the expected contracts for its APIs. The new pipeline also performs a small amount of error checking on behalf of the developer, such as detecting unanticipated calls to async void methods.
Certain features like WebSockets require that this switch be set. Importantly, the behavior of async / await is undefined in ASP.NET unless this switch has been set. (Remember: setting
Causes server controls to render data-val attributes in markup rather than send a snippet of JavaScript for each input element which requires validation. This provides a better extensibility story for customizing how client-side validation is performed. Depending on the complexity of the page, it can also significantly reduce the size of the generated markup.
Selects which version of the .NET Framework's reference assemblies are used when performing compilation. (Note: Visual Studio requires that this element be present in Web.config, even though we auto-infer it.)
Causes ASP.NET to use an enhanced pipeline when performing cryptographic operations. More information on the new pipeline can be found here (links to the first part of a three-part blog series).
Similar to quirks mode, but instead of affecting core runtime behavior it affects how controls render markup. For example, consider the control (note the empty alt tag). In earlier versions of ASP.NET, this stripped the empty alt tag and output , which fails HTML validation. When the rendering compatibility version is set to "4.5", the empty alt tag is preserved, and the output is .
Miscellaneous questions
Can I detect the target framework programmatically?
Yes! Take a look at the HttpRuntime.TargetFramework static property. Keep in mind that this property should only be queried from within an ASP.NET application. Any other access (such as via a console application) could result in an undefined return value.
But what if I don't want the runtime to infer all of those configuration settings?
No problem! You can always explicitly change any setting. For example, you could put this in Web.config:
<httpRuntime targetFramework="4.5" /><pages controlRenderingCompatibilityVersion="4.0" />
Even though
My hoster is displaying "Unrecognized attribute 'targetFramework'" errors!
This error means that you have created an ASP.NET 4.5 application but are trying to deploy it to a server that only has the .NET Framework 4 (not 4.5) installed. Many hosters offer ASP.NET 4.5 as an option. They may require you to contact them first so that they can enable 4.5 for your account.
How does this affect existing ASP.NET 4 applications?
There was no
If there is no
Wrapping Up
I hope I have conveyed the intent and the utility of the
As always, please feel free to leave feedback in the comments!