Locations of visitors to this page

    Blog List       Minimize  
.NET
.NET 3.5
.NET:ACL
.NET:AppDomains
.NET:ASP
.NET:ASP ServerControls
.NET:ASP:Commerce
.NET:ASP:Config
.NET:ASP:JSON
.NET:ASP:Layout
.NET:ASP:Media/Flash
.NET:ASP:Mobile
.NET:ASP:Monitoring
.NET:ASP:Navigation
.NET:ASP:Stress Testing
.NET:ASP:Validation
.NET:ASP:WebParts
.NET:C#-Trig
.NET:CAB
.NET:CAS
.NET:Certification
.NET:CF
.NET:Collections
.NET:Configuration
.NET:Cryptography
.NET:Db
.NET:Delegates
.NET:Deployment
.NET:Diagnostics
.NET:Documentation
.NET:Encoding
.NET:Environment
.NET:Extension Methods
.NET:Globalization
.NET:I/O Streams
.NET:Interop
.NET:IO:Mail
.NET:IsolatedStorage
.NET:LicenseManager
.NET:LINQ
.NET:Metrics/Quality
.NET:Mono
.NET:MSOffice
.NET:Optimization
.NET:Patterns/Practices
.NET:Reflection
.NET:Remoting
.NET:Reverse Engineering
.NET:Serialization
.NET:Silverlight
.NET:Silverlight UserGroup
.NET:Threading
.NET:WCF
.NET:Windows Services
.NET:WinForms
.NET:WPF
.NET:Xml
Admin
Admin:Creating Software
Admin:CruiseControl
Admin:Estimating
Admin:Installers/Packaging
Admin:Methodologies
Admin:PM
Admin:SourceControl
Admin:UnitTesting
Admin:VisualStudio
Arch:Gen
Arch:Patterns
Arch:UML
Blogging
DB:Sqlite
DB:SqlServer
DB:SqlServer CE
DB:VistaDB
Graphs
IT
IT:DNN
IT:DOS
IT:IIS
IT:MailServers
IT:MS Office
IT:OS (XP/Vista/7)
Misc
Misc:Hardware
Misc:Humour
mISV:Accounting
mISV:Marketing
OS:Vista
Personal
Personal:Children
Personal:Faith
Personal:Family
Personal:History
Personal:Politics
Places:New Zealand
Places:Paris
Presentations
Tech:CSS
Tech:Regex
Tech:SQL
Tech:Web:HTML
Tech:XML/XSL

             
    Sprouting Synapses       Minimize  

             
All about ASP.NET Configuration issues (web.config, configuration sections, Providers, etc.) and how to squeeze out performance when needed...

You know when you are debugging a website hosted in IIS, and you attach to a running process:

image

And you put a breakpoint on a gnarly piece of code that’s you’re trying to figure out…and right when you’re getting into the zone…you get interrupted with:

image

GRRRRRARGH!

Chill out…the answer is simple:

use the IIS Manager to navigate to the Application Pool in which you application is running, select Advanced Settings, and then change its Ping Enabled value to False:

 

image

 

That’s it…

Read both these links...that should do the trick.

Hint...Configure your Application Pool (not the website itself) to:

  • Idle Timeout=0,
  • or better yet: Idle Timeout=0, Recycle at midnight, SmartPing at 12.10 to wake it back up for the next day.

Right. That should solve it for you. In case you also want to know more about what SqlServer want's I've added another post that will be helpful.

Links:

http://forums.iis.net/t/1152266.aspx

http://blogs.msdn.com/sqlexpress/archive/2008/02/22/sql-express-behaviors-idle-time-resources-usage-auto-close-and-user-instances.aspx

http://downloads.xact-solutions.com/3rdparty/smartping/SmarterPing_Setup.exe

Visual Studio does it for you automatically, so this doesn't come up too often, but if you need to create the Provider tables in an already existing database, you'll need aspnet_regsql. You'll find it here:

C:\WINDOWS\Microsoft.NET\Framework\<versionNumber>\aspnet_regsql.exe

Using the CommandLine

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Aspnet_regsql.exe 
    -E -S localhost [-d aspnetdb] -A all

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Aspnet_regsql.exe 
    -U me -P justkidding -S localhost [-d aspnetdb] -A all

Flags to use are:

  • -W Runs the tool in wizard mode.
  • -S The name of the server computer running SQL Server. Can also include an instance name, such as .\INSTANCENAME.
  • -U The SQL Server User Name. Needs to be used with (-P) option. Not necessary if using Windows credentials (-E).
  • -P The SQL Server password. Use with (-U) option. Not necessary if using Windows credentials (-E).
  • -E Authenticates using the Windows credentials of the currently logged-in user.
  • -d Name of the database to create or modify. If not specified, the default is "aspnetdb".
  • -C The connection string to the computer running SQL Server where the database is/will be installed. 
        Not necessary if only specifying server (-S) and login (-U and -P, or -E) information.
  • -sqlexportonly filename Generates a SQL script file only.

Using the GUI

The GUI is launched without any command line arguments:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Aspnet_regsql.exe

In order to get this:

image

image

image

image

etc.

 

 

 

 

Links

blog_aspnet_logo_config

This was a really painful lesson to learn at the time (still is ongoing) ...wish I had started off right, at the very beginning, rather than having a lot of code to go back and write alternate solutions for:

Read More »

blog_aspnet_logo_config Karl Seguin has a great post introducing Memcached:




<<<You're probably already familiar with Memcached - it's a highly efficient distributed caching system. It's used generously by all the big web 2.0 players (In may 2007 it was revealed that Facebook relies on 200 16GB quad-core dedicated Memcached servers). Interest in Memcached from the .NET community has been relatively low (although over the last year more and more people are talking about it). Frankly, if you're doing anything that requires horizontal scaling you're seriously shooting yourself in the foot by overlooking it. It runs on windows - although we run it on Linux and there's really no reason for you not to learn that too!

Fundamentally, there are two problems with the built-in cache. First, it's limited to the memory of a single system which happens to be shared with the rest of your application domain. Secondly, if you have two servers, each with their own in-memory cache, users are likely to see very weird synching issues. Memcached isn't as fast as in-memory caching, but will scale to virtually unlimited amount of memory. There isn't any redundancy of failover, simply memory spread across multiple servers.
>>>

aspnet_logo_config  Good to remember:

 

 

<<<In a Web farm, you cannot guarantee which server will handle successive requests. If a user is authenticated on one server and the next request goes to another server, the authentication ticket will fail the validation and require the user to re-authenticate.

The validationKey and decryptionKey attributes in the machineKey element are used for hashing and encryption of the forms authentication ticket. The default value for these attributes is AutoGenerate.IsolateApps. The keys are auto-generated for each application, and they are different on each server. Therefore, authentication tickets that are encrypted on one computer cannot be decrypted and verified on another computer in a Web farm, or in another application on the same Web server.

To address this issue, the validationKey and decryptionKey values must be identical on all computers in the Web farm. For more information about configuring the machineKey element, see How To: Configure MachineKey in ASP.NET 2.0.
>>>

Src: “Explained: Forms Authentication in ASP.NET 2.0”

aspnet_logo_config Need this be mentioned? Probably not…but just in case.

If you want to see debug messages generated by ASP.NET, don’t forget to:

  1. Turn compilation debug message ON (so that the framework produces PDB files)
  2. Turn customErrors OFF so that you can see the debug messages:
<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
  <location inheritInChildApplications="false">
    <system.web>
      ...
      <compilation debug="true"/>
      ...
      <customErrors mode="Off"/>
      ...
      </system.web>
   </location>
</configuration>

 

(And when you’ve finished….reverse both values so that debugging is turned off, and error messages go back to being custom…)

 

<!--
<customErrors mode="RemoteOnly" defaultRedirect="mycustompage.htm"/>
-->


aspnet_logo_config As you may already know, I’ve been writing a set of Generic Db (Sql-92) Providers for Membership, Roles, Profiles, and Personalization, and am nearly done.

 

Since the primary purpose of the Membership and Roles providers is to provide Authentication on your website, and use it to limit access to certain directories (Authorization), I’m going to go over the two parts again below.

Authentication, Roles, and Authorization are distinct  services. Although Authentication is almost always used in conjunction with other services, such as Roles, or Authorization, it is important to recognize that Authentication is only the process of recognizing a returning user to your website, no more, no less.

One can then use other services to assign Roles to an Authenticated user, and/or use Authorization to limit access to specific users and/or roles. Because there is no meaningful way of setting up Authentication without setting up Roles and/or Authorization, they are always discussed together (but that doesn’t make them the same thing).

Authentication The first step is always authentication, which you can setup in one of the following Modes:

None Forms (ie Forms Authentication), Windows (Windows NT authentication, used with any IIS authentication (Basic, Digest, Integrated Windows authentication ( NTLM/Kerberos ), or certificates. Passport (Microsoft Passport Authentication). Intranet websites often use Windows authentication, and 9 times out of 10, websites that are intended for public use will use the Forms authentication mode.

We’ll not be discussing the Windows mode, and instead be focusing only on the Forms mode.

Setting up a Forms Authentication environment To set up Forms authentication environment, we have to do the following steps:

add an authentication element to the web.config file create a login.aspx page, with a asp:login control on it. define a list of Users. The simplest example of such a setup would be the following two snippets:

1. A really rudimentary login.aspx page:

DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> html xmlns="http://www.w3.org/1999/xhtml" > head runat="server"> title>Untitled Pagetitle> head> body> h5>Login Please:h5> form id="form1" runat="server"> div> asp:Login runat="server"/> div> form> body> html>

2. A minimalist root web.config authentication section:

authentication mode="Forms"> forms loginUrl="Login.aspx" protection="All" timeout="30" path="/" /> authentication>

Note that the above authentication section is doing the following:

specifying that the mode=Forms, specifying that the page that non-authenticated users are redirected to is called login.aspx the optional protection attribute is described in full here, but you can safely omit it for most purposes. the timeout is self-evident. the Path is optional, but important to consider (I generally would set it): Specifies the path for cookies that are issued by the application. The default is a slash (/), because most browsers are case-sensitive and will not send cookies back, if there is a path case mismatch. 3. The above is not complete: you’ve defined the system by which to authenticate, but you still need to define an actual list of users/passwords to authenticate against. The simplest solution (but not the best -- see further down) is to define a list of users/passwords directly within the web.config by adding a credentials element to the forms element:

authentication mode="Forms"> forms loginUrl="Login.aspx" protection="All" timeout="30" path="/"> credentials passwordFormat="Clear"> user name="user1" password="0000" /> user name="user2" password="1111" /> credentials> forms> authentication>

Note Obviously this way of adding users/passwords is rudimentary: one can only setup a very small list of users in the web.config, and the list is not dynamic, so the above solution is never really used in a real life situation (one would use a Membership Provider instead, which we will be presenting at the bottom).

How it works - the Authenticate process

We have a login form, and we have a list of users/passwords, but if you try it out, you can click the Login control’s submit button until you are blue in the face without getting past it.

That is because you have a form, and you have a list – but nobody is actually checking against the list.

You do this by by handling the Authenticate event of the Login control, and from there calling a method of the FormsAuthentication static class to check the name and password against the credentials in the system.web file.

MSDN Documentation:

"The Authenticate event is raised when a user uses the Login control to log in to a Web site. Custom authentication schemes can use the Authenticate event to authenticate users." "Custom authentication schemes should set the Authenticated property to true to indicate that a user has been authenticated." "When a user submits his or her login information, the Login control first raises the LoggingIn event, then the Authenticate event, and finally the LoggedIn event." protected void Login1_Authenticate(object sender, AuthenticateEventArgs e){ string userName = uiLogin.UserName; string password = uiLogin.Password; //Note: //By setting the return value, the form //will automatically be Response.Redirected to the original page. e.Authenticated = FormsAuthentication.Authenticate(userName, password); }

If the Authenticate helper method says its ok, remember to set the e.Authenticated return flag, so that the Login control then continues to by building an Identity cookie and redirect the page to the original page, as demonstrated in the following proxy code:

class Login: CompositeControl { void AttemptLogin(string, string){ ..raise. if (args2.Authenticated){ FormsAuthentication.SetAuthCookie( this.UserNameInternal, this.RememberMeSet); this.OnLoggedIn(EventArgs.Empty); this.Page.Response.Redirect(this.GetRedirectUrl(), false); } ... } }

 

The FormsAuthenticationModule

What I haven’t mentioned yet is the FormsAuthenticationModule.

Its part of the lifecycle of all your webpages, since its defined in machine.config:

httpModules> add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/> add name="Session" type="System.Web.SessionState.SessionStateModule"/> add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule"/> add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule"/> add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule"/> add name="RoleManager" type="System.Web.Security.RoleManagerModule"/> add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule"/> add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule"/> add name="AnonymousIdentification" type="System.Web.Security.AnonymousIdentificationModule"/> add name="Profile" type="System.Web.Profile.ProfileModule"/> ... httpModules>



In other words, even if you didn’t know about it…it was already silently processing all page requests.

Of course, up till this point, when Authentication mode != “Forms” it didn’t actually do anything, but now that mode=”Forms”, it is going to process the incoming Request, and its cookies, and convert the round-tripped info within the cookie we created in the previous step, and set the thread’s IPrincipal for the duration of the request...

void Application_AuthenticateRequest(Object sender, EventArgs e) { // Get the authentication cookie string cookieName = FormsAuthentication.FormsCookieName; HttpCookie authCookie = Context.Request.Cookies[cookieName]; if(authCookie == null) { return; //no cookie? Was not authenticated. } // Get authentication ticket and rebuild the principal & identity FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); string[] roles = string[0]; //Rebuild Identity: GenericIdentity userIdentity = new GenericIdentity(authTicket.Name); //Rebuild Principal: GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, roles); //Stick it on the thread: Context.User = userPrincipal; }



 

Very Important: the Cookie business... At this moment you might be wondering why we didn't just set the IIdentity/User for the thread in the login's Authenticate event, and be done with it, rather than this weird business of setting a cookie, redirecting the page, and building the IIDentity from the cookie's information.

It's done this way because you are working in a stateless environment, where every request gets its own new thread. You can set the IIdentity of the thread in the login control's Authenticate event -- but the thread of that request is not going to live long enough to do anybody much good (like all Requests, its going to be terminated at the end of the Request -- in our case right after the Response.Redirect).

So what we are doing is serializing into a cookie enough information for all subsequent next requests/threads to be able to rehydrate a new IIdentity from it.

Got that? Good...

 

With the above 3 parts provided by you (Login control and Form, authentication element, Login control’s Authenticate event handler) and the one part automatically provided by the framework (FormsAuthenticationModule), you have the very basics of Authentication.

But Authentication is almost never used alone: it is practically always used to limit access to certain directories, in other words: authorization.



Authorization Authorization – the process of limiting access to certain directories to certain users and roles – is done by setting up an authorization section in a web.config file. This means:

either placing a web.config in the sub folder itself, with the authorization section in it (here allowing only our two users defined above): xml version="1.0" encoding="utf-8" ?> configuration> system.web> ... authorization> allow users="user1,user2" /> deny users="*" /> authorization> ... system.web> configuration>

or (more commonly) defining an authorization element within  a location element in the root web.config file: location path="PrivateDir1"> system.web> authorization> --> allow users="user1,user2" /> deny users="*" /> authorization> system.web> location> system.web> location>

Notes: Note that the syntax of the web.config authorization element is rudimentary: you allow and deny a list of users, and roles, but the order in which you allow/deny is important (the first rule that matches will be used, so if you [deny ‘*’,allow 'Admin'], rather than [allow 'Admin', deny '*'] not even Admin will have authorization to access the specified location). Note that the path attribute is the name of a directory: “PrivateDir” works, “~/PrivateDir”, “/PrivateDir”, “PrivateDir/”,”./Public” does not. With the above two parts, Authentication and Authorization, you have the very basics required to limit access to certain parts of your website.

 

Roles But there’s more.

Notice that the authorization element has an attribute referring to roles, which we have not addressed yet. There is a reason for this: the authentication system that we’ve already introduced does not manage or deal with roles, as it is beyond its scope of simply verifying that a user knows the associated password.

So who assigns the roles to a curent user (IPrincipal)?

It turns out that roles are only assigned to the current threads’ User (IPrincipal) automatically when the authentication mode=Windows (fair enough: only natural for Microsoft to push their own products and solutions first).

So when the mode is set to Forms, as in this case, you have to assign roles to the current thread’s IPrincipal manually, by code.

This can be done within by handling the the asp:Login authenticate event, or in the asp:Login LoggedIn event.

Set the Roles in the LoggedIn Event We’re already using the authenticate event, and roles has nothing to do with authentication, so we’re going to use the LoggedIn event for this part.

MSDN Documentation:

“The LoggedIn event is raised after the authentication provider checks the user's credentials and the authentication cookie is queued to send to the browser in the next response. Use the LoggedIn event to provide additional processing, such as accessing per-user data, after the user is authenticated.” void onLogin(object sender, EventArgs e) { string[] roles = new string[]{"Admin","Users"};

// Create ticket: FormsAuthenticationTicket authenticationTicket = new FormsAuthenticationTicket(1, txtUserName.Text, DateTime.Now, DateTime.Now.AddMinutes(15), false, roles.Join("|") ); //Encrypt it so that the info is safe //in the client's cache: string encryptedTicket = FormsAuthentication.Encrypt(authenticationTicket); //Convert encrypted string into a cookie: HttpCookie formAuthenticationCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); //Send it out: Response.Cookies.Add(formAuthenticationCookie); //Done with cookie, all that's left to do is get out of here //back to src page: // And send the user where they were heading string originalUrl = FormsAuthentication.GetRedirectUrl(txtUserName.Text, false); Response.Redirect(originalUrl); }



What we’ve done here are the following steps:

we've attempted to authenticate the user, and if authenticated, determined a list of roles for the user, and serialized the roles into a string, and packed the string into a cookie, that is sent out to the client. Hum. You may be wondering how is it that we didn't handle the Authenticate event to work with the underlying web.config credentials in our first examples (ie, how did it know to use the credentials element in the config file), and only handled the event when we wanted to added Roles logic.

I wondered about this too. Opening Reflector I found out that

 

How Cookies are used to give an illusion of a stateless thread User Why so complicated?! Why not just set the roles in the current thread's principal and be done with it? Simply because its a stateless environment. If you set the IPrincipal of the thread, the thread will be terminated at the end of the Request for the login page. So by the time it is redirected to the original page the user came from, it is no longer the same thread, and therefore not the same user: this newer IPrincipal would have an empty roles list.

For this reason we put into a cookie what is needed to rebuild a new Principal when needed by a new thread.

The cookie round trips back at the beginning of the new page, and and we use the info in the cookie to rebuild the IIDentity and IPrincipal by handling the AuthenticateRequest event handler within the global.asax file:

void Application_AuthenticateRequest(Object sender, EventArgs e) { // Get the authentication cookie string cookieName = FormsAuthentication.FormsCookieName; HttpCookie authCookie = Context.Request.Cookies[cookieName]; if(authCookie == null) { return; //no cookie? Was not authenticated. } // Get authentication ticket and rebuild the principal & identity FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value); string[] roles = authTicket.UserData.Split(new Char [] {'|'}); //Rebuild Identity: GenericIdentity userIdentity = new GenericIdentity(authTicket.Name); //Rebuild Principal: GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, roles); //Stick it on the thread: Context.User = userPrincipal; }

If you want to keep a list of passwords/users in an xml file, exterior to the web.config, read this post.

 

 

Its done by code, as is shown here.

To learn how to do this, with the above redimentary system, you may want to read this article.

 

Membership, Roles Providers

I said ‘rudimentary’ above, because one would never release a web site with the above simplistic solution of reading a list of users/passwords from the web.config file.

Rather, one would use the built in Membership and Roles providers (NET 2.0), which are generally backed by a database.

1.Setup a membership Provider:

membership defaultProvider="SQLiteMembershipProvider"> providers> clear/> add name="SQLiteMembershipProvider" applicationName="/" connectionStringName="SQLiteConnStr" type="XAct.Web.Providers.DbMembershipProvider, XAct.Web.Providers.DbProviders"/> add name="SqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="SqlServerConnStr" applicationName="/" minRequiredPasswordLength="5" minRequiredNonalphanumericCharacters="0" /> providers> membership>

The above declarative code shows that we are adding two providers (the default SqlServer one, and our custom Sqlite/Generic Db one).

MSDN Documentation of setting up the membership element.

2.Setup a Roles Provider:

roleManager enabled="true" defaultProvider="SQLiteRoleProvider"> providers> add name="SQLiteRoleProvider" applicationName="/" connectionStringName="SQLiteSecurityConnStr" type="XAct.Web.Providers.DbRoleProvider, XAct.Web.Providers.DbProviders"/> providers> roleManager>

MSDN Documentation on roleManager element.

Both are automatically wired up. When you use Reflector, you can see that that the asp:login control is already wired up to the Membership provider’s ValidateUser method:

//The login control: class Login { private void AttemptLogin(){ ... AuthenticateUsingMembershipProvider(e); ... } private void AuthenticateUsingMembershipProvider(AuthenticateEventArgs e){ e.Authenticated = LoginUtil.GetProvider(this.MembershipProvider). ValidateUser(this.UserNameInternal, this.PasswordInternal); } }

If the user is validated, the Login control sets the cookie and redirects to the original calling page:

FormsAuthentication.SetAuthCookie( this.UserNameInternal, this.RememberMeSet);

So far this is very similar to what we were doing before (see the login_authenticate(object sender, EventArgs e) example above).

Not only is the Membership provider automatically wired up to set the cookie, and the create an IIdentity on the thread, but so is the Roles Manager.

The Roles Manager is wired up to the PostAuthenticate event to replace the GenericIdentity that was created by the MembershipProfile above, with a new extended RolePrincipal:

System.Web.Security.RoleManagerModule { ... private void OnEnter(object source, EventArgs eventArgs) if (!(context1.User is RolePrincipal)){ context1.User = new RolePrincipal(context1.User.Identity); } application1.SetPrincipalOnThread(context1.User); } ... }

As you can see, this is almost identical wiring as our example above, where we used a cookie to round-trip the roles that the user is in, except that this time, the whole thing was wired up for you.

 

Adding/Removing Users and Roles Now that you’ve installed the Providers, and all Users and Roles are defined in a database, you have a scalable website, but you lose the ease of adding users and roles by simply editing the system.web file.

Instead, you have to use the ASP.NET Web Site Administration Tool, which is available from the menu of Visual Studio, to add users and assign roles to them.

The default WSAT is pretty horrible (slow and with deal-breaking restrictions for a daily use)…a better (but not free) one can be purchased from QualityData, or you can roll your own by following this article…or maybe do better by looking at this work.

Using Directory Authorization with the Users and Roles Managers Although you’ve moved the Authentication system to a Db based solution above, you still manage authorization with the web.config as we’ve already demonstrated.

Is that all the power we’ve got??! No…we have much more.

Obviously, for...

Read More »

aspnet_logo_config I’ve already mentioned in a previous post how you can use the web.config to add a reference to a specific assembly to all pages in the website, saving you a ton of typing, and making subsequent changes much easier:

<configuration>
  <system.web>
  <pages masterPageFile="/MasterPages/site.master" smartNavigation ="true">
      <controls>
        
        <add tagPrefix="AJAXTOOLKIT"  
            namespace="AjaxControlToolkit" 
            assembly="AjaxControlToolkit"/>

        <add tagPrefix="XACT"  namespace="XAct.Web.Controls" 
            assembly="XAct.Web.Core, Version=1.0.0.0, 
            Culture=neutral,PublicKeyToken=null"/>
        <add tagPrefix="XACT"  namespace="XAct.Web.Controls" 
            assembly="XAct.Web.Controls.CodeFragment,PublicKeyToken=null"/>
      control>
  pages>

But I forget to mention that another time saver is to add import statements to all pages in the same way (I’m always finding that i have to manually add the missing Generic collections namespace, etc):

<system.web>
    <pages>    
        <namespaces>
            <add namespace="System.Collections.Generic" />
        </namespaces>
    </pages>    
</system.web>

You can also assign a base page to all pages – which is a fine way to add authorization code to all pages in one go:

<system.web>
    <!-- ... -->
    <pages pageBaseType="XAct.Web.Apps.MyAuthorizedPageBase" />
    <!-- ... -->
</system.web>

aspnet_logo_config I am always forgetting that its referred to as “app_code”:

 

 

<<<
If you want to specify any type in web.config that is located in your App_Code directory and does not have a namespace, simply use:

<add type="MyCustomType, App_Code" />. 

That will work if you are using the web site projects.>>>

aspnet_logo_config “It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.”

Cause #1:
This can happen when you move the site from your work rig to a final location because when you start a  new web project Visual Studio automatically creates Virtual Directories and configures them as Applications for you.

If you then manually create a Virtual Directory, and don’t configure it to be an APplication, you will get the above error. Hence the hint that it gives
("This error can be caused by a virtual directory not being configured as an application in IIS.").

The Fix:
Right-click the the Virtual Directory within IIS, select Properties, and select Create next to the Application.
It will upgrade the Virtual Directory to an Application, (using the name of the Virtual Directory).

Cause #2:

There are many reasons to use secondary web.config files in nested directories. One is to use the configuration/system.web/pages directive to set a new master page for all nested pages, or to limit access to the files therein, by updating the configuration/system.web/authorization section for just that dir and its children.

That will work.

But there are other sections that cannot be defined in nested config files…

hence the part of the message that states “It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level

The Fix:
In such cases you will have no other option but to move the definitions to the Application web.config level.

Cause #3:
A little more arcane: the above error message can also happen when you are updating a project within Visual Studio, as stated in this post and maybe better, here.

What’s happening here is that VS saves a copy of the original website in a Backup folder within the project directory.

This leads to asp.net raising the above error because its seeing the web.config  in the backup file – which being nested within the root application web.config file, is now being thought of as nested web.config file, with sections defined lower down than allowed (ie: Cause#2).

The Fix:
Delete or move out the Backup Folder…

aspnet_logo_config If you nest two Applications, and therefore their web.config files, you will probably run into this situation.

If a parent/root web.config file refers to a type (eg, a config section Type) it will expect to find it in its Bin dir. So far so good.

If you nest another Application web.config file in it, it will enherit from its parent Application web.config the reference.

What this means is that the child Application will expect to find the Type in its Bin, and not find it, throwing an error.

The Fixes:

 

Fix#1: GAC: (not a great solution)
Put the assembly in GAC so that its visible from any location.
Obviously, this solution is not a great one under all circumstances.

Fix#2: Duplication:(not a good solution)
Copy the assembly from the root Assembly bin to the child Assembly bin…

Fix#3:Restructure and use the Remove attribute.

To save myself some typing, you could read this post, but the summary of it is that you will have to manually clear or remove the offending references from the child web.config.

In the following example (from the above post) where the child Application is nested within a DNN Application, you have to remove refs to the DNN’s assemblies that are not in the child’s bin dir or the GAC:

<httpModules>
      <!-- add name="Authentication"
        type="DotNetNuke.HttpModules.AuthenticationModule,
        DotNetNuke.HttpModules.Authentication" / -->
      <remove name="UrlRewrite"  />
      <remove name="Exception" />
      <remove name="UsersOnline" />
      <remove name="DNNMembership" />
      <remove name="Personalization" />
    </httpModules>
   <httpHandlers>
     <!-- This is for FTB 3.0 support -->
     <remove verb="GET" path="FtbWebResource.axd"/>
     <!-- This is for CAPTCHA support -->
     <remove verb="*" path="*.captcha.aspx"/>
     <!-- This is for Serving files, secure, insecure, from database -->
     <remove verb="*" path="LinkClick.aspx"/>
   </httpHandlers>

   <pages validateRequest="false" 
        enableViewStateMac="true" 
        enableEventValidation="false">
     <namespaces>
       <remove namespace="DotNetNuke.Services.Localization" />
       <remove namespace="DotNetNuke.Entities.Users" />
       <remove namespace="DotNetNuke" />
       <remove namespace="DotNetNuke.Common" />
       <remove namespace="DotNetNuke.Data" />
       <remove namespace="DotNetNuke.Framework" />
       <remove namespace="DotNetNuke.Modules" />
       <remove namespace="DotNetNuke.Security" />
       <remove namespace="DotNetNuke.Services" />
       <remove namespace="DotNetNuke.UI" />
       <remove namespace="DotNetNuke.Entities.Portals" />
       <remove namespace="DotNetNuke.Common.Utilities" />
       <remove namespace="DotNetNuke.Services.Exceptions" />
       <remove namespace="DotNetNuke.Entities.Tabs" />
     </namespaces>
   </pages>

 

Notes:

  1. Note that this is the class solution under NET 1.1
  2. Not to be confused with what is going on here because that person is talking about a nested web.config, true, but not a nested Application web.config. 
  3. That said, this solution doesn’t cover everthing, as pointed out in this post which states:
    <<<
    Most collections in the web.config have the <remove> tag or <clear> tag to remove irrelevant modules or handlers in the child app.  The problems I've found occur with settings relating to the <pages> tag, which most items in it don't support <remove>. This means if your child applications doesn't share or have the same registered controls, masterpages or themes then you are probably going to have issues or be forced to specify the settings on a per-page basis.  This is where the inheritInChildApplications="false" really comes in handy.
    >>>

 

Fix#4: inheritInChildApplications=false (the right solution)
Which brings us to (finally!) the point of this post…

The way to solve it is by using the inheritInChildApplications flag on on a location element within the root Application config file:

<location inheritInChildApplications='false'>
    <system.web>
       ...
    </system.web>
</location>

Note: this element + attribute is only available from NET 2.0, but that shouldn’t be a problem now a days.

MSDN Documentation:
SectionInformation.InheritInChildApplications Property.

location Element


UPDATE:
While researching further, I came across this post which adds an important point – namely that just because you can do it, doesn’t mean that you always want to apply without thinking carefully about what you doing… you don’t want to stop inheritence of all elements.

<<<
…use that feature with care and ensure that you only use it for sections that you really do want to isolate (e.g., <httpHandlers />, <httpModules />, and maybe <authentication />, depending on your setup).  Keep sections like <compilation /> and <customErrors /> outside of that location tag so they'll be inherited, or you'll need to redefine them in sub-applications.
>>>

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

Vastly under-utilized, too many pages have the import statements directly at the top of the page:

<%@ Page Language="C#" AutoEventWireup="true" 
    MasterPageFile="~/MasterPages/Demo.master" %>

<%@ Register TagPrefix="XACT" Namespace="XAct.Web.Controls" 
    Assembly="XAct.Web.Core" %>

Which is not really anybody's fault: its how the documentation of NET 1.1 came out...

But really...it's SOOO much easier to stick it directly in the web.config like this, once and for all:

<configuration>
  <system.web>
  <pages masterPageFile="/MasterPages/site.master" smartNavigation ="true">
      <controls>
        
        <add tagPrefix="AJAXTOOLKIT"  
            namespace="AjaxControlToolkit" 
            assembly="AjaxControlToolkit"/>

        <add tagPrefix="XACT"  namespace="XAct.Web.Controls" 
            assembly="XAct.Web.Core, Version=1.0.0.0, 
            Culture=neutral,PublicKeyToken=null"/>
        <add tagPrefix="XACT"  namespace="XAct.Web.Controls" 
            assembly="XAct.Web.Controls.CodeFragment,PublicKeyToken=null"/>
      control>
  pages>

Try it...just once...you'll never go back.

image

GenericDbProfileProvider: 43 passed, 0 failed, 0 skipped.


Only have to sweat still over the PersonalizationsProvider and I can relax a bit I think...

As I've already mentioned, I'm working on some SQL-92 compliant custom providers for ASP.NET

Yes...I know someone has already tried to port one the providers to other databases already:

My problem with the first is not worth going into, and my first gripe about the second is that there was no PersonalizationProvider.
But my main gripe is that its too specific to a DBMS, not allowing for different connection string settings.

Specifically, I'm thinking of 2 areas where a better, less coupled set of providers, could end up being useful:

  • using Sqlite, and now SqlServer CE, for quick web demo jobs.
  • NET 3.5 WinForm applications now have an option to make them ASP.NET membership/role compatible... I haven't looked into this, but I certainly wouldn't want to if I had to install SqlServer Express on client machines ...SqlServer CE would be much better...

So I decided to write a complete set (all 5 providers) with no specific db in mind (who knows which other ones will appear on the market) -- using only SQL-92, and providing enough config settings that it can be made to work with any DBMS...  Also, make it well enough designed that a custom one could be created quickly for a specific DBMS (eg sprocs, etc).

 

And I'm slowly getting there...
Tonight's test results:

image

GenericDbMembershipProvider Tests: 25 passed, 0 failed, 0 skipped.
GenericDbRolesProvider Tests: 35 passed, 0 failed, 0 skipped.

I've already written (but not vigorously run) the next 3 providers (GenericDbProfilesProvider, GenericDbPersonalizationProvider, and GenericDbSiteMapProvider), although I still need to document, refactor, and write tests for them.

But at least, for tonight, I'm happy with the results I' getting...


If this project holds interests to you, drop me a line of encouragement =:-)

Copyright 2007 by Sky Sigal