Cumbersomeness Web.Config

| Comments

People who are in .NET arena might encounter the plethora of xxx.config files especially web.config.  It is a required devil for placing any configuration settings for your applications.  We should appreciate the effort taken by the engineers at Microsoft for introducing (at that time, Java’s configuration approach is very immature) and providing a declarative approach, centralized and optimistic way of configuring your .NET application of any size.  This has well fitted for applications written for simple purpose including training codes, in-house tools, etc.  In a real-world, irrespective of small, medium or large scale, both Visual Studio and developers place lots of stuff on this mess sourrounded by <, >, />, ” characters. 

When you see the web.config schema, it accomodates the following types of configuration for an application: 

  • Configuration section declaration (your custom configuration section)
  • Startup settings
  • Runtime settings such as impersonation, app domain manager, GC, CAS publisher policy, etc
  • Remoting settings
  • Network settings like authenticatio modules, proxy settings, System.Net, connection related
  • Tracing and debug
  • Application settings
  • Connection strings
  • ASP.NET settings like anonymous identification, authentication and authorization, caching, compiling, custom errors, health monitoring, HTTP runtime, handlers and modules, membership
  • WCF settings like service endpoint, service behavior and binding configurations

Bit sleepy?  Refer http://msdn.microsoft.com/en-us/library/ms731734.aspx for WCF and http://msdn.microsoft.com/en-us/library/dayb112d.aspx for ASP.NET specific settings.  

There are machine wide settings those are overridden by site and application wide settings also.  Does this really prouductive in development stage and helpful in production stage?  Having experience on real world .NET applications, the main concerns with web.config are: 

  • Readability & Understandability.  Though it has the capability of defining the whole earth even the whole universe, readability is very bad.  Mentally, it outshines the content clarity.

Let us see a sample web.config for a WCF service.  How easy for you to add some more settings related to WCF tracing? 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?xml version="1.0"?>
<configuration>
 <configSections>
 <section name="WcfSaml" type="Udooz.Security.CustomTokenConfiguration, Udooz.Security"/>
 </configSections>
 <appSettings/>
 <connectionStrings/>
 <system.web>
 <compilation debug="true">
 <assemblies>
 <add assembly="System.IdentityModel.Selectors, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
 <add assembly="System.Security, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
 <add assembly="Microsoft.Transactions.Bridge, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
 <add assembly="SMDiagnostics, Version=3.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
 <add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
 <add assembly="System.Web.RegularExpressions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
 <add assembly="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
 <add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
 <add assembly="System.ServiceProcess, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/></assemblies></compilation>
 <authentication mode="Windows"/>
 <!--
 <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
 <error statusCode="403" redirect="NoAccess.htm" />
 <error statusCode="404" redirect="FileNotFound.htm" />
 </customErrors>
 -->
 </system.web>
 <system.diagnostics>
 <trace autoflush="true"></trace>
 <sources>

 <source name="System.ServiceModel.MessageLogging">

 <listeners>
 <add name="xml" type="System.Diagnostics.XmlWriterTraceListener"/>
 </listeners>
 </source>
 </sources>
 </system.diagnostics>
 <system.serviceModel>
 <diagnostics>

 <messageLogging
 logMalformedMessages="false"
 logMessagesAtServiceLevel="false"
 logEntireMessage="false"
 logMessagesAtTransportLevel="false"/>
 </diagnostics>

 <services>
 <service
 behaviorConfiguration="ServiceBehavior"
 name="SampleService.HelloWorldService">
 <endpoint binding="wsFederationHttpBinding"
 address=""
 bindingConfiguration="ServiceBinding"
 contract="SampleService.IHelloWorld"/>
 <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
 </service>
 </services>
 <bindings>
 <customBinding>
 <binding name="MutalCertificateBinding">
 <security authenticationMode="MutualCertificate"
 requireSecurityContextCancellation ="false"
 requireSignatureConfirmation="false"
 messageProtectionOrder ="SignBeforeEncrypt"
 requireDerivedKeys="true">
 </security>
 <httpTransport/>
 </binding>
 </customBinding>

 <wsFederationHttpBinding>
 <binding name="ServiceBinding">
 <security mode="Message">
 <message issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" negotiateServiceCredential="false">
 <!--<claims>
 <add claimType="http://schemas.udooz.net/2010/12/identity/claims/EmailAddress"/>
 <add claimType="http://schemas.udooz.net/2010/12/identity/claims/GivenName"/>
 <add claimType="http://schemas.udooz.net/2010/12/identity/claims/Surname"/>
 </claims>-->
 <issuer address="http://localhost/SamlSecurityTokenService/SamlTokenIssuer.ashx" bindingConfiguration="UsernameBinding" binding="customBinding">
 <identity>
 <dns value="UdoozDotNetServerCert"/>
 </identity>
 </issuer>
 </message>
 </security>
 </binding>
 </wsFederationHttpBinding>
 </bindings>
 <behaviors>
 <serviceBehaviors>
 <behavior name="ServiceBehavior" returnUnknownExceptionsAsFaults="false">
 <serviceCredentials type="Udooz.Security.CustomCredentials, Udooz.Security">
 <serviceCertificate findValue="CN=UdoozDotNetServerCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName"/>
 <clientCertificate>
 <authentication revocationMode="NoCheck"/>
 </clientCertificate>
 </serviceCredentials>
 <serviceMetadata httpGetEnabled="true" />
 </behavior>
 </serviceBehaviors>
 </behaviors>
 </system.serviceModel>
</configuration>

The above configuration is for a federated WCF web service.
When you refer the alternate representations of XML, you can feel the readability issue.  Here is, an example JSON taken from Wikipedia: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
 "firstName": "John",
 "lastName": "Smith",
 "age": 25,
 "address":
 {
 "streetAddress": "21 2nd Street",
 "city": "New York",
 "state": "NY",
 "postalCode": "10021"
 },
 "phoneNumber":
 [
 {
 "type": "home",
 "number": "212 555-1234"
 },
 {
 "type": "fax",
 "number": "646 555-4567"
 }
 ]
 }

The same in XML: 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<person>
 <firstName>John</firstName>
 <lastName>Smith</lastName>
 <age>25</age>
 <address>
  <streetAddress>
   21 2nd Street
  </streetAddress>
  <city>
   New York
  </city>
  <state>
   NY
  </state>
  <postalCode>
   10021
  </postalCode>
 </address>
 <phoneNumbers>
  <phoneNumber type="home">
   212 555-1234
  </phoneNumber>
  <phoneNumber type="fax">
   646 555-4567
  </phoneNumber>
 </phoneNumbers>
</person>

The same in YAML which is very famous in Rails arena: 


firstName:     John
lastName:      Smith
age:     25
address:
    streetAddress:   21 2nd Street
    city:      New York
 state:     NY
 postalCode:      10021

phoneNumbers:
    – type:   home
      number:   212 555-1234
 
 - type:   fax
      number:   646 555-4567

  • Managability – the user who might be a developer, solution architect, production administrator have to manage lot of stuff to accomplish an intent.  For example, WCF security related settings are scattered across service binding (security mode settings) and behavior (e.g. service credential settings) sections.  Thought technically this is the correct way, however from an user perspective, this has lesser managability.
  • Mixture Settings. since web.config is the only best place for any configuration, the “separation of concerns” principle couldn’t be achieved in various stages of configuration.  For example, some settings are more helpful during development stage, for example, the list of claims types can be understood by a WCF service, assemblies for handling custom authentication, etc.  But these configuration are no way helpful, but making this as configuration is harmful to the application at the production stage.  Just think that changing “GivenName” claim to “GivenName2″ in the above sample web.config might break the system.

What is expected?

Yes, we should appreciate the level of flexibility and extensibility available in .NET.  However, in the era of agile, productivity and simplicity are matters.  It is time to adopt and follow “convention over configuration” approach those are main part in Rails and Android development world.  If required, let the user (again developer or solution architect) override the convention. 

Instead of putting everything on the messy XML tags, provide developer/solution architect/administrator friendly DSL, which would be internal DSL on C# or easy to learn external DSL.  For example, in Rails, to map a web request to appropriate REST action is being configured by Ruby syntax.

1
2
3
4
5
6
ActionController::Routing::Routes.draw do |map|
 map.connect products/:id, :controller => catalog,
 :action => view'

 #...other routings
end

In the above code, you see such mapping.  These kind of scripting will be more favourable than the awkwardness XML.