Archives

Archives / 2006 / July
  • Common Gotcha: Slow VS 2005 Web Site Build Performance Because of “Dueling Assembly References”

    September Update: For more details on how to optimize web project build performance in VS 2005, please review this blog post I did here. 

    Symptom:

    When building a VS 2005 Web Site Project you experience very slow build times – where compilation seems to mysteriously pause for 30+ seconds (or even minutes), before continuing each time you do a build or F5 of the project.

    This compilation performance slow-down might be the result of a problem I call “dueling assembly references”.  This can occur when you setup multiple “automatic refresh enabled” file-system assembly references within your web site project to multiple assemblies that in turn share common assembly dependencies whose version numbers are different.  The post below describes this in more detail and how to fix it.

    Some background on how file-assembly references work:

    Developers using VS 2005 Web Site Projects can add references to assemblies in multiple ways.  When you right-click on a project and choose the “Add Reference” menu option, it provides a dialog like the one below that allows you to reference .NET assemblies registered in the GAC, class libraries built as projects in the same VS solution, COM components, and file-path based assemblies that you pick using the “Browse” tab of the “Add References” dialog:

    When you add a file-system based assembly reference using the “Browse” dialog above, VS 2005 Web Sites will copy the assembly picked into the \bin directory of the project as well as (by default) add a .refresh file into the \bin directory that contains a relative path string that points back to the original assembly path location:

    When you build the solution, VS will automatically check those assemblies with .refresh files to see if a newer version of the assembly is available, and if so automatically re-copy the assembly and re-compile the solution using it.  This avoids you having to manually update the assembly yourself every time it changes (it also avoids you having to check-in the assembly under source control).  If you don’t want this “automatic refresh” capability, you can delete the .refresh file – in which case VS will not check the timestamp nor update the assembly for you.

    Because assemblies often have dependent assembly references, Visual Studio will also automatically copy dependent assemblies that a reference assembly requires into the \bin directory of the target web-site as well.  For example: if you setup a file-based reference to ClassLibrary1.dll, which in turn uses an assembly called SharedLibrary.dll that resides in the same directory, Visual Studio will make sure both assemblies are copied into the Web Site’s \bin directory (note: if it didn’t do this then the app would fail to run).

    Dueling Assembly Reference Problem

    The problem I refer to as a “dueling reference” occurs when you setup multiple file-based references to assemblies from a VS 2005 Web Site Project that are each updated dynamically (using .refresh files), and which in turn have dependencies on different versions of a common shared assembly library.  

    For example, assume your web-site has a reference to AssemblyA.dll and AssemblyB.dll – which in turn each reference AssemblyC.dll.  This will work without problems if the referenced AssemblyC.dll is the same version for both AssemblyA.dll and AssemblyB.dll.  But it will cause problems if the AssemblyC.dll being used is different between the two:

    In cases where AssemblyC.dll is different, VS ends up copying the AssemblyC.dll file twice for each build – since it continually thinks that the assembly has been updated (once for each reference).  This ends up requiring all references to be recalculated by the compiler, and a full re-build to occur within the IDE.  This will cause build performance to slow down dramatically, and will cause builds to appear to pause as VS does this reference recalculation and fix-up on every single build.

    How to Fix This Problem

    There are a couple of ways to fix this problem:

    1) The “most correct” way to fix this issue is to make sure your class library references are built against the same version of any dependent assemblies.   This is good to-do not just to fix the above build performance problem, but also because it decreases the likelihood of introducing hard to figure out bugs in your application (at runtime only one version of the shared assembly is going to be used –so if you don’t fix this at least one of your dependent assemblies will end up running against an assembly it wasn’t built/unit-tested with).

    2) The “easiest quick fix” way to resolve this issue is to modify one or more of your assembly file-based references to not be “automatic refresh enabled”.  You can do this by deleting the .refresh files within your \bin directory that produce that shared conflict.  This will avoid having VS auto-update the assemblies, and so will prevent the dueling update conflict altogether.   You might find it useful to quickly disable this behavior by deleting the .refresh files as a stop-gap, and then re-enable the auto-refresh behavior once you fix the shared assembly conflicts.

    Note that VS 2005 Web Application Projects don’t have this dueling reference issue (since they don’t use .refresh files for file-assembly references).  So you won’t have this build-performance problem with it (instead it will just pick one version of the shared assembly to use).   However, you still want to be careful about cases where you have two components built against two separate versions of a shared assembly – since this can still cause hard to understand behavioral bugs at runtime that end up bypassing your carefully written unit-tests (which were run against a different version of the shared assembly).

    Hope this helps,

    Scott

  • Atlas “How Do I?” Video Series Begins

    Earlier this spring we published a really popular series of short, focused, online ASP.NET videos that we called the “How Do I?” series.  The series walked through how to use Visual Web Developer to take advantage of the cool new ASP.NET 2.0 features like Master Pages, Membership, Role Management, Profiles, 3-tier Data Access, Themes, Web Parts, etc.

    If you haven’t watched these videos yet, I'd highly recommend setting aside an hour or two to review them all (each video is only 10-15 minutes long).  They are a really great way to quickly obtain a very good understanding of the new ASP.NET 2.0 features and how they all fit together.  You can watch each video online for free here.

    The series was so popular that we asked Joe Stagner to join the ASP.NET team fulltime to help us produce even more.  Our plan is to have him publish a brand new ASP.NET “How Do I?” video on the www.asp.net web-site each week going forward from now on.

    The first set of videos that Joe is building out will cover how to use Atlas to build Ajax applications with ASP.NET.   This week he published the first two videos on this:

    You can watch them both on the www.asp.net site from this page.  Make sure to check back each week to watch new videos as they get published.

    Hope this helps,

    Scott

    P.S. For other good Atlas Samples + Tutorials, please read my previous post on Atlas Tutorials + Samples here.

    P.P.S. AlanL from Vertigo has been posting some really nice tutorials on how to use some of the controls within the Atlas Control Toolkit.  I’d recommend checking out these two great posts to read: Atlas: Creating Better Looking Checkboxes with the ToggleButtonExtender and Atlas: Creating a Confirmation Using the ModalPopup Extender

     

  • Recipe: Implementing Role Based Security with ASP.NET using Windows Authentication and SQL Server

    Problem

    You are building an Intranet expense report application for your organization, and want to enable role-based authentication and authorization capabilities within it.  Specifically, you want to create logical roles called “approvers”, “auditors”, and “administrators” for the application, and grant/deny end-users access to functionality within the application based on whether they are in these roles.

    Because your application is an Intranet solution, you want to use Windows Authentication to login the users accessing the application (avoiding them having to manually login).  However, because the roles you want to define are specific to your application, you do not want to define or store them within your network’s Windows Active Directory.  Instead, you want to define and store these roles within a database.  You then want to map Windows user accounts stored within Active Directory to these roles, and grant/deny access within the application based on them.

    In addition to using roles to authorize access to individual pages within the application, you want to dynamically filter the links displayed within the site’s menu navigation based on whether users have permissions (or not) to those links.  And lastly, you want to build-in a custom role-management administration UI directly within the expense report application for “expense app administrators” to manage these roles and control who has access to the capabilities of the app:

     

    Solution 

    I've put together a detailed post that walks through step-by-step how to implement all of this.  You can read it here, and download the completed sample I walk through how to build here.

    Hope this helps,

    Scott

  • Recipe: Implementing Role-Based Security with ASP.NET 2.0 using Windows Authentication and SQL Server

     

    Problem

    You are building an Intranet expense report application for your organization, and want to enable role-based authentication and authorization capabilities within it.  Specifically, you want to create logical roles called “approvers”, “auditors”, and “administrators” for the application, and grant/deny end-users access to functionality within the application based on whether they are in these roles.

    Because your application is an Intranet solution, you want to use Windows Authentication to login the users accessing the application (avoiding them having to manually login).  However, because the roles you want to define are specific to your application, you do not want to define or store them within your network’s Windows Active Directory.  Instead, you want to define and store these roles within a database.  You then want to map Windows user accounts stored within Active Directory to these roles, and grant/deny access within the application based on them.

    In addition to using roles to authorize access to individual pages within the application, you want to dynamically filter the links displayed within the site’s menu navigation based on whether users have permissions (or not) to those links.  And lastly, you want to build-in a custom role-management administration UI directly within the expense report application for “expense app administrators” to manage these roles and control who has access to the capabilities of the app:

    The below post walks through step-by-step how to implement all of this.  You can download and run the completed sample we’ll build below here.

    Discussion

    ASP.NET provides a flexible authentication management system that allows you to easily take advantage of Windows authentication to identify “who” the authenticated user accessing your site is.   You can learn how to enable this and how it works by reviewing my previous Recipe: Enabling Windows Authentication within an Intranet ASP.NET Web Application.

    Most web applications typically have at least dozens (if not thousands or millions) of authenticated users accessing them.  Authorizing access to pages or functionality within a site is difficult (if not impossible) when managing it on an individual user account level.  Instead, it is much better for the application developer to define higher level “roles” to map users into, and then grant/deny access or permissions based on these roles.

    For example: if we were building an internal expense reporting application, we might want to create three roles for the application: “approvers”, “auditors” and “administrators”.   We would then only allow users in the “approvers” role to access the portion of the site that enables someone to approve employee expenses.  We would only allow users in the “auditors” role to access the portion of the site that generates reports and analysis on employee spending.  And we would only allow the select few users in the “administrators” role to have access to the admin pages of the application.

    By coding against roles like “approvers”, “auditors” and “administrators”, as opposed to individual account names, we can have very clean code within our application, and make the application very flexible as we add/remove users to the system and change their permissions over time.  For example, if “Joe” gets promoted to be a manager with expense approver permissions, all that needs to happen for him to get access to the approver portion of the expense app is for the administrator to go in and add him into the “approvers” role – no code changes or configuration changes to the app need to be made.

    ASP.NET supports multiple places where user to role mappings can be stored and defined.  When Windows Authentication is enabled, ASP.NET will by default use the Active Directory user/group mappings to support role access permission checks.  This is useful when the permission checks you want to perform are global to your company environment.

    For many applications, though, you might want to implement more local role policies – where the roles you define are specific to the application.  In cases like these you either might not want to store these in a central Active Directory store, or your network administrator might not even allow it.   Instead, you might want to store the role definitions and user mappings locally within a database – while still using Windows Authentication to identify and login the users stored within them. 

    The below walkthrough demonstrates how to-do this, and builds a complete end-to-end application to illustrate how all the pieces fit together.

    Solution

    Step1: Create a New Web Site

    Use Visual Studio or Visual Web Developer (which is free) to create a new ASP.NET 2.0 web site/project.

    We'll start by creating a new external CSS stylesheet called “Stylesheet.css”.  We'll use this to style the application consistently.  Add the below CSS rules to it:

     

    body {

        font-family:Arial;

    }

     

    .loginname {

        font-size:small;

        color:Red;   

    }

     

    .menu {

        float:left;

        width:200px;

    }

    .roleList

    {

        margin-top:10px;

        margin-bottom:10px;

    }

    We'll then add a new file called “Web.SiteMap” in the top level "root" directory of the project.  SiteMap files enable us to specify the site hierarchy and link structure that we want to use to organize pages within our site, and can be used to databind Menus and navigation UI against (treeviews, breadcrumb controls, etc).  Within the Web.SiteMap file add this XML to define the link structure for the site we are going to build:

    <?xml version="1.0" encoding="utf-8" ?>

     

    <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

     

        <siteMapNode url="default.aspx" title="Home">

            <siteMapNode url="approver.aspx" title="Approver Expenses" />

            <siteMapNode url="audit/auditor.aspx" title="Audit Page" />

            <siteMapNode url="admin/admin.aspx" title="Admin Manager" />

        </siteMapNode>

     

    </siteMap> 

     

    Then we'll create a Master Page template called “Site.Master” at the root of the project.  This will enable us to define an overall consistent layout to use for all pages on the site.  Within it we'll add a Treeview control that is databound to our site navigation hierarchy that we defined above, and which will provide a hierachical menu for navigating the pages in our site.  Define the contents of the Site.Master page like so:

    <%@ Master Language="VB" CodeFile="Site.master.vb" Inherits="Site" %>

     

    <html>

    <head runat="server">

        <title>Expense Report</title>

        <link href="StyleSheet.css" rel="stylesheet" type="text/css" />

    </head>

    <body>

        <form id="form1" runat="server">

       

        <div class="header">

            <h1>

                Expense Report Sample

                <asp:LoginName ID="LoginName1" FormatString="(Welcome {0})" CssClass="loginname" runat="server" />

            </h1>

        </div>

                      

        <div class="menu">

       

            <asp:TreeView ID="T1" DataSourceID="SiteMapDataSource1" runat="server" ImageSet="Simple" EnableViewState="False" NodeIndent="10">

                <ParentNodeStyle Font-Bold="False" />

                <HoverNodeStyle Font-Underline="True" ForeColor="#DD5555" />

                <SelectedNodeStyle Font-Underline="True" ForeColor="#DD5555" HorizontalPadding="0px" VerticalPadding="0px" />

                <NodeStyle Font-Names="Verdana" Font-Size="8pt" ForeColor="Black" HorizontalPadding="0px" NodeSpacing="0px" VerticalPadding="0px" />

            </asp:TreeView>

           

            <asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />       

       

        </div>

       

        <div class="content">

       

            <asp:contentplaceholder id="MainContent" runat="server">

            </asp:contentplaceholder>

       

        </div>

       

        </form>

    </body>

    </html>

    Note: for convenience sake I’m using the “Simple” auto-format selection option within the VS designer to style the TreeView control above.  One downside is that this is embedding inline styles.  To enable a pure CSS styling solution for the TreeView above I would want to download and use the ASP.NET 2.0 CSS Control Adapter Toolkit. 

    After we define the Site.Master template, we’ll create a new page called “Default.aspx” that is based on the master-page and that we will use as the home-page for the site.  Create the page like so:

    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">

     

        <h3>Some typical home-page content would go here...</h3>

     

    </asp:Content>

    And then run the page.  You will see a page like this:

    Note that the tree-view’s site navigation links on the left-hand side of the page are databound from the ASP.NET 2.0 Site Navigation System.

    Next create two subdirectories in the project called “admin” and “audit” and add pages within these sub-directories called “admin.aspx” and “auditor.aspx” respectively.  Also then create a top-level page called “approver.aspx” in the root directory of the site.  All of these pages should be based on the Site.Master template we defined above.

    When you are done with this your project’s solution explorer will look like this:

    Now we are ready to enable Windows Authentication and add Role Support to the site.

    Step 2: Enable Windows Authentication

    To enable Windows Authentication for our expense report web-site above, and force users to always be authenticated when visiting the application, we’ll want to open our web.config file at the root of the project and add this XML to it:

       <authentication mode="Windows" />

     

       <authorization>

          <deny users="?"/>         

       </authorization>

    To understand better what this does, please take a few moments to review my previous Recipe: Enabling Windows Authentication within an Intranet ASP.NET Web Application.

    Once we’ve configured the settings above, every user to the site will automatically be validated and authenticated via our Windows authentication system on the network. 

    Step 3: Enable SQL based Role Management

    ASP.NET 2.0 ships with built-in role manager providers that work against SQL Express, SQL Server, and Active Directory (which can also be used against ADAM stores).  If you’d prefer to use your own custom database (or the file-system or your own LDAP system), you can also build your own role manager provider and easily add it to the system.  This web-site details how the ASP.NET 2.0 Provider Model works, how you can build your own providers to plug-in, and enables you to download the source code for the built-in providers that ASP.NET ships with.

    For the purposes of this sample we are going to use either SQL Express or SQL Server to store our role mappings.  To begin with we’ll want to enable the ASP.NET role-manager in our web.config file.  We’ll do this by adding this section within the <system.web> group:

      <roleManager enabled="true"/>

     

    If you have SQL Express installed on your machine, then you are done.  ASP.NET will automatically provision a new SQL Express database within your app_data folder at runtime that has the appropriate Roles tables configured to persist the role mappings we’ll do.  You don’t need to take any additional steps to configure this.

    If you don’t have SQL Express installed, and instead want to use a SQL Server to store the Roles data, you’ll need to create a database within SQL to store the ASP.NET Application Service tables, and update your web.config file to point at the database.  The good news is that this is easy to-do.  If you haven’t done this before, please read my previous Configuring ASP.NET 2.0 Application Services to use SQL Recipe that demonstrates how to do this.  Below is a sample configuration entry that shows how to configure the web.config file to use a SQL database:

      <roleManager enabled="true" defaultProvider="SqlRoleManager">

       

        <providers>

          <clear/>

          <add name="SqlRoleManager"

               type="System.Web.Security.SqlRoleProvider"

               connectionStringName="SqlRoleManagerConnection"

               applicationName="MyApplication" />

        </providers>

     

      </roleManager>

    Where the “SqlRoleManagerConnection” connection-string we referenced above is defined within the <connectionStrings> section of our web.config file like so:

      <connectionStrings>

        <add name="SqlRoleManagerConnection"

             connectionString="Data Source=localhost;Initial Catalog=BasePageSample;Integrated Security=SSPI;">

        </add>

      </connectionStrings>

    Important: If you explicitly declare a new provider reference like I did within the <providers> section above, you need to make sure that you specify the “applicationName” attribute.  Otherwise you’ll run into this problem that I discuss in this blog post here.

    And now, when we run our application again, ASP.NET will automatically authenticate the incoming users to the site using Windows Authentication, and use the SQL Database we defined above to retrieve all role mappings.

    Step 4: Create Our Roles and Initial User Mappings to Them

    Now that we have configured our role provider, we can use the “Roles” API (the System.Web.Security.Roles class) within ASP.NET to create roles, and manage users within them. 

    One tip/trick I like to use is to take advantage of the “Application_Start” event handler within Global.asax to setup my Roles if they don’t already exist, and map any initial users into them if necessary.   To-do this, choose File->Add New Item and select the “Global.asax” file item.  Then add this code to your Application_Start event handler:

        Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)

           

            If (Roles.RoleExists("Auditors") = False) Then

                Roles.CreateRole("Auditors")

            End If

           

            If (Roles.RoleExists("Approvers") = False) Then

                Roles.CreateRole("Approvers")

            End If

           

            If (Roles.RoleExists("Admins") = False) Then

                Roles.CreateRole("Admins")

                Roles.AddUserToRole("REDMOND\scottgu", "Admins")

            End If

           

        End Sub

    This event handler gets called once every-time the ASP.NET application starts up.  As you can see above, within it I am checking to see whether our three roles exist in the configured Roles database – and if not I create them.  I also then added my Windows account into the “Admins” role that I created last.  This is a good approach you can use to setup the initial admin users for your application.

    Now, when we run the application again, it will automatically provision the three new roles into the database and add me into the admin one (note: in the code above I’ve only added myself to the admin role – I’m not yet in the Auditors or Approvers role).

    Step 5: Authorizing Access Based On Roles

    To verify that our roles were setup correctly, let’s add some authorization rules to grant/deny access to portions of the site based on them.  I can do this using the “authorization” section of our web.config files.

    First create new “web.config” files within both the “admin” and “audit” directories:

    Then add the below XML content within the web.config file within the “admin” directory:

       <?xml version="1.0"?>

     

       <configuration>

     

          <system.web>

     

       <authorization>

          <allow roles="Admins"/>

          <deny users="*"/>

       </authorization>

         

          </system.web>

     

       </configuration>

    This tells ASP.NET to allow users within the “Admins” role to access the pages within the directory, but to block everyone else who isn’t in this role.  We’ll then want to add similar content to the web.config file within the “audit” directory (which does the same logical thing as above – except in this case only allowing “Auditors” access).

    For the “Approver.aspx” page that is in the root directory of the application we’ll want to-do something a little extra.  Because it isn’t in a sub-directory of its own, we can’t use a global directory rule like we did above.  Instead, we’ll want to restrict the rule to only apply to the “Approver.aspx” page within that directory.  We can do this by specifying a <location path=”url”> directive around it within our root web.config file on the site:

        <location path="Approver.aspx">

     

          <system.web>

     

            <authorization>

              <allow roles="Approvers"/>

              <deny users="*"/>

            </authorization>

     

          </system.web>

         

        </location>

    And now when we run the application again, and try to access the “Audit Page” or “Approver Page” within the site we’ll get this error message:

    This happens because when we created the roles in our Application_Start event handler we didn’t add ourselves into the “Approvers” or “Auditors” role – and so ASP.NET is denying us access to those resources (like it should).  When we click the “Admin” link we are able to access it, though, because we belong to the “Admins” role.

    Step 6: Implementing Security Trimming for our Menu

    One issue you will have noticed when we ran the sample above is that the menu on the left-hand side of the screen is still displaying all of the links when we visit the site – including links to the “Audit” and “Approver” pages that we don’t currently have access to (because we aren’t in those roles). 

    Ideally we want to hide those links and only display them to users within the appropriate roles to access them.  This avoids users inadvertently seeing our “Access Denied” error message above.  The good news is that this is easy to implement using a cool ASP.NET 2.0 feature called “Security Trimming”.

    To implement security trimming, add this XML section to your web.config file:

          <siteMap defaultProvider="XmlSiteMapProvider" enabled="true">

            <providers>

              <add name="XmlSiteMapProvider"

                description="Default SiteMap provider."

                type="System.Web.XmlSiteMapProvider "

                siteMapFile="Web.sitemap"

                securityTrimmingEnabled="true" />

            </providers>

          </siteMap> 

    This tells ASP.NET to enable security-trimming at the Site Navigation provider.  Once we do this, we can then open up our web.sitemap configuration file again and update the individual nodes within it with “roles” attributes indicating what nodes are visible to incoming users:

    <?xml version="1.0" encoding="utf-8" ?>

     

    <siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

     

        <siteMapNode url="default.aspx" title="Home">

            <siteMapNode url="approver.aspx" title="Approver Expenses" roles="Approvers" />

            <siteMapNode url="audit/auditor.aspx" title="Audit Page" roles="Auditors" />

            <siteMapNode url="admin/admin.aspx" title="Admin Manager" roles="Admins" />

        </siteMapNode>

     

    </siteMap>

    And now when I run the application again, you’ll notice that the “Audit” and “Approver” links are hidden from the TreeView (since I’m still not in those roles):

    The “administrator” link is still available, however, because I am a member of the “Admins” role.

    Step 6: Building our Admin Page to Manage Role Permissions

    The final step we’ll want to take is to build a simple role-manager page that Administrators within our expense reporting application can use to Add/Remove users from roles.  We’ll do this within the “admin.aspx” page underneath our “Admin” sub-directory (which only users within the “Admins” role can access).  Within this page we’ll add a TextBox that administrators can use to lookup a username, and then use a checkbox list to enable administrators to easily add/remove the selected user from roles.  When the admin clicks the “Update” button these changes will be persisted in the role database:

    Here is what the .aspx markup for the admin.aspx page looks like to implement this:

    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">

     

        <h3>Role Manager</h3>

     

        <div>

            Enter UserName:

            <asp:TextBox ID="TxtUserName" runat="server"></asp:TextBox>

            <asp:Button ID="LookupBtn" runat="server" Text="Search" /> 

        </div>

     

        <div class="roleList">

            <asp:CheckBoxList ID="RoleList" runat="server">

            </asp:CheckBoxList>

        </div>

       

        <div>

            <asp:button ID="UpdateBtn" text="Update" Visible="false" runat="server" />

        </div>

       

    </asp:Content>

    Notice that I’m using an <asp:checkboxlist> control to provided a list of checkboxes for the roles.  Here is then the complete code-behind file for the page:

    Partial Class Admin_Admin

        Inherits System.Web.UI.Page

     

        Sub PopulateRoleList(ByVal userName As String)

     

            RoleList.Items.Clear()

     

            Dim roleNames() As String

            Dim roleName As String

     

            roleNames = Roles.GetAllRoles()

     

            For Each roleName In roleNames

     

                Dim roleListItem As New ListItem

                roleListItem.Text = roleName

                roleListItem.Selected = Roles.IsUserInRole(userName, roleName)

     

                RoleList.Items.Add(roleListItem)

     

            Next

     

        End Sub

     

        Sub UpdateRolesFromList()

     

            Dim roleListItem As ListItem

     

            For Each roleListItem In RoleList.Items

     

                Dim roleName As String = roleListItem.Value

                Dim userName As String = TxtUserName.Text

                Dim enableRole As Boolean = roleListItem.Selected

     

                If (enableRole = True) And (Roles.IsUserInRole(userName, roleName) = False) Then

                    Roles.AddUserToRole(userName, roleName)

                ElseIf (enableRole = False) And (Roles.IsUserInRole(userName, roleName) = True) Then

                    Roles.RemoveUserFromRole(userName, roleName)

                End If

     

            Next

     

        End Sub

     

        Sub LookupBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles LookupBtn.Click

            PopulateRoleList(TxtUserName.Text)

            UpdateBtn.Visible = True

        End Sub

     

        Sub UpdateBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UpdateBtn.Click

            UpdateRolesFromList()

            PopulateRoleList(TxtUserName.Text)

        End Sub

     

    End Class

    Note that the above code does not hardcode the list of roles used within the application.  Instead it dynamically populates the <asp:checklistbox> with the list of all roles currently in the Role Manager.  This means that if we add a new role to our application it will automatically show up in the list (it also means you should be able to copy/paste this page into any application with Roles enabled and take advantage of it).

    Note also that if you lookup your own account, and enable either the “auditors” or “approvers” role for it, the Treeview menu for the site will now show those nodes that were previously unavailable to us:

    Programmatically Looking Up Roles

    In the example above, I’ve been using the <authorization> section within web.config files to control access to page resources based on the role of the user.  This works well for page-level authorization, however sometimes you want to grant everyone access to a page and then perform role-level checks within the page to control capabilities (for example: showing/hiding the edit and delete buttons within GridViews, etc). 

    Developers can use the User.IsInRole(rolename) method to easily check to see whether an authenticated user is within a specific role within a page.  For example:

            If User.IsInRole("Administrators") Then

                ' Do something only admins are allowed to-do

            End If

    This works great from within ASP.NET pages.  Non-page derived classes don’t have a built-in “User” object.  However, you can always access an ASP.NET Request’s User identity context using code like this:

            Dim User As System.Security.Principal.IPrincipal

            User = System.Web.HttpContext.Current.User

     

            If User.IsInRole("Administrators") Then

                ' Do something only admins are allowed to-do

            End If

    The above code will work in any class within your application (or in a referenced class library assembly).

    Summary

    The above walkthrough demonstrates how you can build an application from scratch using ASP.NET 2.0’s built-in Windows Authentication, Role Management, Master Pages, Site Navigation, Url Authorization, and Security Trimming features.  The combination of these features makes building secure Intranet applications easy.  You can download a completed version of the above sample here.

    For additional reading I’d recommend checking out these links:

    My ASP.NET 2.0 Security Resource Link Page: This is a good page to bookmark for ASP.NET 2.0 Security information, and contains ton of tutorials and links on ASP.NET security topics.

    ASP.NET 2.0 “How Do I” Videos: This is a great series of short 10-15 minute videos that you can watch online to learn ASP.NET concepts.  Included are several videos that show off Security, Roles, Master Pages and Site Navigation.

    How To: Use Role Provider in ASP.NET 2.0: This is a great article on MSDN that walks-through how to use the ASP.NET 2.0 Role Features.

    Role Providers in ASP.NET: This page from K. Scott Allen contains some additional information on the ASP.NET Role Providers.

    ASP.NET Site Navigation Tutorials: This is a great set of tutorials by Scott Mitchell that discuss the ASP.NET Site Navigation features.

    Hope this helps,

    Scott

     

  • Recipe: Dynamic Site Layout and Style Personalization with ASP.NET

    Problem:

    You want to enable end-users visiting your web-site to dynamically customize the look and feel of it.  Specifically, you want to enable them to choose different content layouts for the site, as well as different style experiences (colors, fonts, etc). 

    When building this customization system, you want to make sure you avoid duplicating logic throughout the multiple pages.  Instead, you want to be able to author the dynamic personalization logic once and have all pages inherit it.

    Solution:

    ASP.NET 2.0 makes dynamically customizing the UI experience of a web-site easy.  Specifically, it provides a flexible mechanism for defining page layouts using a feature called “Master Pages”.  It also provides a flexible mechanism for defining a page’s style look and feel via a feature called “Themes”. 

    What is nice about both “Master Pages” and “Themes” is that they can be configured either statically (by specifying the Master Page or Theme to use within an ASP.NET Page’s <% Page %> directive), or dynamically at runtime by setting the Page’s MasterPageFile and Theme properties via code.  This later approach can be easily used to enable dynamic user personalization of UI on a web-site.  The below walkthrough demonstrates how to implement this.  You can also download a complete sample that shows how to-do this here.

    Step 1: Create a New Web-Site

    Begin by starting a new web-site in either Visual Studio or Visual Web Developer (which is free).  Create two master page files called “Site1.Master” and “Site2.Master” within it. 

    We will use these two master-pages to provide two alternative layout views for the site that end-users will be able to pick between.  For the purposes of this sample we’ll keep each master file simple (although obviously you could add much more layout and content to each).  Here is a trivial example of what you could have in “Site1.Master” to start with for the purposes of this sample:

    <%@ Master Language="VB" CodeFile="Site1.master.vb" Inherits="Site1" %>

     

    <html>

    <head runat="server">

        <title>Site1 Template</title>

    </head>

    <body>

        <form id="form1" runat="server">

        <div>

            <h1>Site1 Master Template</h1>

       

            <asp:contentplaceholder id="MainContent" runat="server">

            </asp:contentplaceholder>

        </div>

        </form>

    </body>

    </html>

    After creating the two master-pages, add two new ASP.NET pages to the project – and choose to have them built using a Master Page.  Call these two new pages “Default.aspx” and “Personalize.aspx”, and have them be based on the Site1.Master template above. 

    Step 2: Add Two Themes to the Site

    Right-click on the Solution Explorer within VS and choose the “Add ASP.NET Folder->Theme” context menu option.  This will create a folder called “App_Themes” immediately underneath your application root directory.  You should then create two sub-folders underneath the “App_Themes” folder called “Blue” and “Red”.  This will define two separate themes for the site.  Within each theme folder you can add CSS stylesheets, images, and .skin files to customize the site’s appearance however you want.

    For the purposes of this sample, we’ll add a “blue.css” stylesheet under the “Blue” folder and a “red.css” stylesheet under the “Red” folder.  For now we’ll keep their implementations trivial and just set the background color for pages in them:

    body

    {

        background-color:blue;

    }

    Once this is done, our site project layout should look like this:

    Now we are ready to enable end-users visiting our site to dynamically personalize which Master Page file is used (allowing them to control the layout of the site), as well as which Theme/Stylesheet is used (allowing them to control the style of the site).

    Step 3: Enable ASP.NET Personalization

    We could save our visitors’ layout and style preferences in a variety of different places (within an http cookie, within a custom database, in a directory on the file-system, etc).  For the purposes of this sample, I’m going to use the new ASP.NET 2.0 Profile Personalization feature.  This allows me to easily save/retrieve information about users accessing the site with minimal code.

    You can use the ASP.NET Profile Personalization feature in combination with any authentication mechanism (Windows Authentication, Forms Authentication, Passport, or any other authentication approach you want).  For this sample I’m just going to use Windows Authentication to login and identify the user.  If you aren’t familiar with how Windows authentication works, please read my previous Enabling Windows Authentication Recipe.

    Once I’ve configured the site to use Windows Authentication, I can enable the ASP.NET Profile system by adding a <profile> section within my web.config file that lists the properties I want to store about users.  For the purposes of this sample I’m going to store two string properties:

    <profile>

      <properties>

        <add name="ThemePreference" type="string" defaultValue="Blue"/>

        <add name="MasterFilePreference" type="string" defaultValue="~/Site1.master"/>

      </properties>

    </profile>

    If you have SQL Express installed on your machine, then you are done.  ASP.NET will automatically provision a new SQL Express database within your app_data folder at runtime that has the appropriate Profile tables configured to save your profile data above.  You don’t need to take any additional steps to configure this.

    If you don’t have SQL Express installed, and instead want to use a SQL Server to store the Profile data, you’ll need to create a database within SQL to store the ASP.NET Application Service tables, and update your web.config file to point at the database.  The good news is that this is easy to-do.  If you haven’t done this before, please read my previous Configuring ASP.NET 2.0 Application Services to use SQL Recipe that demonstrates how to do this.

    Step 4: Building the Personalize.aspx Page

    Now that we have enabled ASP.NET Profile Personalization, we can go to work building a Personalize.aspx page that we can use to enable end-users on the site to dynamically pick which Master Page and Theme they want to use.  To do this we’ll add two <asp:dropdownlist> controls to the page to enable users to select the appropriate choices:

    <asp:Content ID="Content1" ContentPlaceHolderID="MainContent" Runat="Server">

     

        <h2>

            Pick your master file:

            <asp:DropDownList ID="ddlMasterFilePreference" runat="server">

                <asp:ListItem Text="Site Choice One" Value="~/Site1.Master" />

                <asp:ListItem Text="Site Choice Two" Value="~/Site2.Master" />

            </asp:DropDownList>

        </h2>

     

        <h2>

            Pick your theme preference:

            <asp:DropDownList ID="ddlThemePreference" runat="server">

                <asp:ListItem Text="Blue" />

                <asp:ListItem Text="Red" />

            </asp:DropDownList>

        </h2>

     

        <div>

            <asp:Button ID="UpdateBtn" runat="server" Text="Update" />

        </div>

     

    </asp:Content>

    Within the code-behind of the Personalize.aspx page we’ll then write this code to save and restore the selection to the ASP.NET Profile system:

    Partial Class Personalize

        Inherits System.Web.UI.Page

     

        Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

     

            If (Page.IsPostBack = False) Then

     

                ddlMasterFilePreference.SelectedValue = Profile.MasterFilePreference

                ddlThemePreference.SelectedValue = Profile.ThemePreference

     

            End If

     

        End Sub

     

        Sub UpdateBtn_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UpdateBtn.Click

     

            Profile.MasterFilePreference = ddlMasterFilePreference.SelectedValue

            Profile.ThemePreference = ddlThemePreference.SelectedValue

     

            Response.Redirect("default.aspx")

     

        End Sub

     

    End Class

    Notice that ASP.NET automatically adds a strongly-typed “Profile” object to our pages that we can use to easily get type-safe access to the Profile collection we defined within our web.config file previously.  It automatically handles saving/retrieving the user’s profile data from the configured profile database.

    When the Personalize.aspx page is visited, an end-user will now be able to pick which layout and style choice they want:

    When the end-user clicks “Update” their settings will be persisted in the ASP.NET Profile database automatically.  When they revisit the Personalize.aspx page later, their preferences will be retrieved and populated within the dropdownlists.

    Step 5: Building a Base Page Class to Personalize Page Content

    We want to avoid having to repeatedly add the same logic to set the Master Page and Theme on every page of the site.  We’ll instead create a base page class that encapsulates this functionality, and have each of the pages on our site inherit from it. 

    To-do this, we’ll create a new class called “MyBasePage” that will live within our app_code directory (if we are using the VS 2005 Web Site Project option):

    Imports System.Web.UI.Page

     

    Public Class MyBasePage

        Inherits System.Web.UI.Page

     

        Protected Overrides Sub OnPreInit(ByVal e As System.EventArgs)

     

            MyBase.OnPreInit(e)

     

            Dim MyProfile As System.Web.Profile.ProfileBase

            MyProfile = HttpContext.Current.Profile

     

            Me.MasterPageFile = MyProfile.GetPropertyValue("MasterFilePreference")

            Me.Theme = MyProfile.GetPropertyValue("ThemePreference")

     

        End Sub

     

    End Class

    The above class inherits the base ASP.NET Page Class (System.Web.UI.Page), and overrides the OnPreInit method (which is called when the “PreInit” event on a page executes).  Within this method we then set the page’s “MasterPageFile” and “Theme” properties with the values the user persisted within the ASP.NET Profile Store. 

    Note that when accessing the Profile data in the code above, I’m accessing it via the “GetPropertyValue()” helper method.  This code will work both within the web-site project, as well as any companion class-library projects I might want to use to store this base class in instead (enabling me to easily re-use it across multiple projects).

    Step 6: Using the Base Page Class within Pages

    Using the base page class we built above with pages is simple.  Just open the code-behind file of any of the pages within the site, and update its base class declaration from:

    Partial Class _Default

        Inherits System.Web.UI.Page

     

    End Class

    To instead be:

    Partial Class _Default

        Inherits MyBasePage

     

    End Class

    Now when the pages within our site run, the OnPreInit method within our base class will execute, and we’ll dynamically update the Page’s Master Page and Theme to reflect what the user has personalized.  For example:

    Or:

    This is all of the logic we need to enable dynamic layout and style personalization for the site.  We can now pass off the master page templates and CSS style-sheets to a designer to customize to make them as rich and attractive as we want.

    Summary

    The above walkthrough illustrates the basics of how you can enable rich site layout and style personalization.  It also demonstrates how you can encapsulate common page functionality within a base class that you can easily re-use across multiple pages in a site – a very useful approach that can be used far beyond just personalization.

    Click here to download a complete version of the sample that you can run and examine locally.

    Below are also some links/resources I recommend checking out to learn more about ASP.NET Master Pages, Themes, and Personalization:

    ASP.NET 2.0 Website Programming : Problem – Design – Solution: This is an outstanding book by Marco Bellinaso, and uses a great “real world example” approach to walkthrough how to best take advantage of ASP.NET 2.0 features (including Master Pages, Themes, Profiles and more).

    ASP.NET “How Do I” Videos: This is a great series of short 10-15 minute videos that you can watch online to learn ASP.NET concepts.  Included are several videos that show off Master Pages, Themes and Profiles.

    ASP.NET Master Page Articles: Two great articles by K. Scott Allen that talk about both the basics and the advanced scenarios involving ASP.NET 2.0 Master Pages.

    Free ASP.NET Design Template Downloads: Professionally designed site templates for ASP.NET that are XHTML compliant, and provide re-usable Master Pages and Themes that you can use in your own projects.

    Profiles in ASP.NET 2.0: A great article by K. Scott Allen about the new ASP.NET 2.0 Profile System.

    ProfileView Control: Joshua Flanagan has implemented a cool control that you can use in ASP.NET to provide an editing view of your profile personalization data.  You can download the control + its source code from this link.

    ASP.NET 2.0 SQL Table Profile Provider: An alternative Profile Provider implementation for ASP.NET 2.0 that allows you to save Profile properties in schematized Tables or SPROCs (rather than the default storage mechanism which is an XML blob).

    VS 2005 Web Application Project Profile Support: Available as a free download, this utility works with VS 2005 Web Application Projects and enables strongly-typed Profile objects to be used within them.

    Hope this helps,

    Scott

  • Upcoming Free ASP.NET Team Webcasts -- "Live from Redmond"

    Earlier this spring the ASP.NET Team presented several great MSDN web-casts.  The response to those was so great that we've scheduled a bunch of new ASP.NET 2.0 talks over the next two months.  Below is a list of them, along with a registration URL that you can use to sign-up to watch them for free: 

  • Building a Photo Tagging Application using ASP.NET 2.0, LINQ, and Atlas

     

    Over the last few days I’ve spent some spare time playing around with LINQ and LINQ for SQL (aka DLINQ) – both of which are incredibly cool technologies. 

     

    Specifically, I’ve been experimenting with building a photo management application that provides “tagging” support.  If you aren’t familiar with photo tagging, you might want to check out FlickR – which implements a tagging mechanism that enables users to easily annotate pictures with textual words that provide a way to easily organize and sort them.

     

    For my photo-browser I wanted to implement a “tag cloud” on every page of the site that lists the most popular tags in use on the left-hand side, and enables users to be able to click a tag within the “tag cloud” to filter the pictures by them:

     

     

    When a user clicks on an individual picture I then wanted them to see a more detailed version of the picture, as well as all the tags it has been annotated with:

     

     

    I then wanted end-users to be able to click on the “edit tags” link on the page to switch into an edit mode using Ajax, where they can easily create or edit new tags for the picture.  Any word can be suggested:

     

     

    When the tags are saved, I wanted the “tag cloud” to dynamically update with the current list of tags being used across all photos on the site, and size each tag within the cloud based on the tag’s frequency (higher usage frequency produces bigger font sizes for that tag):

     

     

    Implementing the above application is really easy using ASP.NET 2.0, LINQ and Atlas, and provides a nice demonstration of some of the really cool new data capabilities that LINQ enables.

     

    Click Here to Read My Step-By-Step Tutorial of How to Build the Above Application using LINQ, ASP.NET 2.0 and Atlas (full source code provided)

     

    Hope this helps,

     

    Scott

     

     

  • Building a Photo Tagging Site using ASP.NET 2.0, LINQ, and Atlas

     

    Over the last few days I’ve spent some spare time playing around with LINQ and LINQ for SQL (aka DLINQ) – both of which are incredibly cool technologies. 

     

    Specifically, I’ve been experimenting with building a photo management application that provides “tagging” support.  If you aren’t familiar with photo tagging, you might want to check out FlickR – which implements a tagging mechanism that enables users to easily annotate pictures with textual words that provide a way to easily organize and sort them.

     

    For my photo-browser I wanted to implement a “tag cloud” on every page of the site that lists the most popular tags in use on the left-hand side, and enables users to be able to click a tag within the "tag cloud" to easily filter the pictures by them:

     

     

    When a user clicks on an individual picture I then wanted them to see a more detailed version of the picture, as well as all the tags it has been annotated with:

     

     

    I then wanted end-users to be able to click on the “edit tags” link on the page to switch into an edit mode using Ajax, where they can easily create or edit new tags for the picture.  Any word can be suggested:

     

     

    When the tags are saved, I wanted the “tag cloud” to dynamically update with the current list of tags being used across all photos on the site, and size each tag within the cloud based on the tag’s frequency (higher usage frequency produces bigger font sizes for that tag):

     

     

    It turns out that implementing the above solution is very easy using ASP.NET 2.0, LINQ and Atlas.  The below post walks through a simple sample to illustrate the basics.  You can also download a completed version of the sample to try out on your own (details on how to-do this are at the end of this post). 

     

    Step 1: Creating our Database

     

    I started the application by first creating a database within SQL Server to model albums, photos, and photographers, as well as tag annotations mapped against them.

     

    Here is the schema I ended up using for this particular sample (I’ll be adding more properties as I expand the photo management app more in future posts – but this is a basic start):

    You’ll notice that I have an “Albums” and “Photos” table to store common metadata associated with pictures.  I am also using a Photographers table to store photographer names.  The Photos table has foreign-key (FK) relationships against both it and the Albums tables.

     

    I then created a “Tags” table to track tag annotations for the photos.  The Tags table has a foreign-key relationship to the Photos table, and maintains a separate row for each Tag used on the site.  One benefit of this approach is that it makes adding new Tags super fast and easy (you don’t need to worry about duplicate tag-name insert collisions, and you can add and associate new tags in one database round-trip). 

     

    In addition to providing referential integrity, the foreign-key from the Tags table to the Photos table also enables cascading deletes – so that if a photo is deleted the tags associated will automatically be deleted too (avoiding cases of “dangling tags” left in the database that are no longer associated with the data they were associated against).

     

    Step 2: Creating our LINQ for SQL data model

     

    Once my database was built, I created a new LINQ-enabled ASP.NET web-site by selecting the LINQ template that is installed by the LINQ May CTP within the “New Website” dialog in Visual Web Developer (the free IDE for ASP.NET development).  This sets up an ASP.NET 2.0 project that has LINQ fully configured, and allows me to easily use LINQ to connect against databases.

     

    I then created a LINQ-enabled object model that I could use to interact with the database defined above.  The next release of Visual Studio will provide a really nice WYSIWYG designer for creating and mapping this database object model.  In the meantime, I just used the command-line “sqlmetal” utility that ships with LINQ to quickly create this.  All I had to-do was to open up a command-line console and type the following commands to accomplish this:

    >> cd c:\Program Files\LINQ Preview\Bin

    >> 

    >> sqlmetal /database:PhotoDB /pluralize /namespace:PhotoAlbum /code:c:\Projects\PhotoApp\app_code\PhotoDB.cs

    This created a LINQ-enabled object model for the “PhotoDB” database on my local system and stored the generated classes within the “PhotoDB.cs” file within the ASP.NET LINQ project.  Note that the classes generated are declared as “partial types” – meaning developers can optionally add additional properties/methods to them in separate files (common scenarios: entity validation or helper methods). 

     

    Once this LINQ-enabled object model is created, I can then easily use this LINQ-enabled object model anywhere within my ASP.NET application.  For example, to create two new Albums in the Albums table I could just write this code:

     

            PhotoDB photoDb = new PhotoDB();

     

            Album album1 = new Album();

            album1.AlbumName = "Africa Trip";

     

            Album album2 = new Album();

            Album2.AlbumName = "Europe Trip";

     

            photoDb.Albums.Add(album1);

            photoDb.Albums.Add(album2);

     

            photoDb.SubmitChanges();

     

    When the “SubmitChanges()” method is called above, the album instances above are saved into the SQL Server database within the Albums table – without me having to write any raw SQL or data access code.  The above code is all that needed to be written for this to work.

     

    Step 3: Using LINQ to work with data and Tag Photos in our application

     

    LINQ makes working with data a real joy.  It automatically handles relationships, hierarchy, and tracking changes within our model – eliminating tons of data access code. 

     

    For example, to create a new album and a photo within it, I could extend our previous code-sample like this:

     

            PhotoDB photoDb = new PhotoDB();

     

            Photographer photographer1 = new Photographer();

            photographer1.PhotographerName = "Scott Guthrie";

     

            Photo photo1 = new Photo();

            photo1.Description = "Picture of Lion";

            photo1.Url = "http://someurl";

            photo1.Thumbnail = "http://thumbnailurl";

            photo1.Photographer = photographer1;

     

            Album album1 = new Album();

            album1.AlbumName = "Africa Trip";

            album1.Photos.Add(photo1);

     

            photoDb.Albums.Add(album1);

      photoDb.SubmitChanges();

     

    This is all of the code needed to add a new Photographer, Photo and Album into the database, setup the FK relationship between the Photographer and Photo, and setup the Photo and the Album FK relationship (notice how this relationship is expressed by adding the photo into the Album’s photo collection, and by setting the Photographer property on Photo).  Because these properties and collections are strongly-typed, we get full compile-time checking and intellisense of our syntax and data relationships.

     

    C# has also added new syntax support that can be used to make object initialization even terser/cleaner than what I did above.  Specifically, it now allows developers to declare properties (and collections) using syntax like below if you prefer:

     

            PhotoDB photoDb = new PhotoDB();

     

            Photographer scott = new Photographer() {

       PhotographerName = "Scott Guthrie"

                                 };

     

            photoDb.Albums.Add( new Album() {

                                    AlbumName = "South Africa",

                                    Photos = {

                                        new Photo() {

                                            Description = "Lion Close Up",

                                            Photographer = scott,

                                            Url = "http://url1",

                                            Thumbnail = "http://thumb1",

                                        },

                                        new Photo() {

                                            Description = "Zebras at Dusk",

                                            Photographer = scott,

                                            Url = " http://url2",

                                            Thumbnail = " http://thumb2",

                                        }

                                    }

                                } );

     

    photoDb.SubmitChanges();

     

    This is syntactically equivalent to the code before – except that we are now able to compact more functionality in fewer lines of code.  In the example above, I’m now adding two new photos to the new South Africa album (with me as the photographer for both pictures).

     

    Because we setup FK relationships between the Photo table and the Tags table, we get automatic association linking between them with LINQ (this is expressed via the “Tags” property on Photos and the corresponding “Photo” property on each Tag).  For example, I could use the below code to fetch one of our newly created Photo’s above from the database and associate three new Tags to it:

     

            PhotoDB photoDB = new PhotoDB();

     

            Photo photo = photoDB.Photos.Single(p => p.Description=="Lion Close Up");

     

            photo.Tags.Add( new Tag() { Name="Lion" } );

            photo.Tags.Add( new Tag() { Name="AndersH" } );

      photo.Tags.Add( new Tag() { Name="ScottGu" } );

     

            photoDb.SubmitChanges();

     

    I could then use the below code to retrieve a Photo and output its tags within a page:

     

            PhotoDB photoDB = new PhotoDB();

     

            Photo photo = photoDB.Photos.Single(p => p.Description=="Lion Close Up");

     

      foreach (Tag tag in photo.Tags) {

                Response.Write("Tag : " + tag.Name);

            }

     

    I could also then write this code to easily retrieve all Photos that are tagged with a specific tag-name, and output the Photo description and photographer name for each of them:

     

                  PhotoDB photoDb = new PhotoDB();

     

            string tagName = "Lion";

     

            var photos = from photo in photoDb.Photos

                         where photo.Tags.Any(t => t.Name == tagName)

                         select photo;

     

            foreach (Photo photo in photos) {

                Response.Write("Photo: " + photo.Description + " by: " + photo.Photographer.PhotographerName);

            }

     

    I do not need to write any extra data code to make the above code work.  LINQ handles all of the SQL statement execution for me.  This provides an incredibly flexible and elegant way to perform data access.

     

    Step 4: Adding “Tag Cloud” UI to our Application

     

    After defining the database and creating the LINQ-enabled object-model above, I focused on the UI of the site.

     

    To maintain a consistent layout and look and feel across the site, I first created an ASP.NET master page that I called “Site.Master”.  Within this file I defined the basic layout structure that I wanted all pages to have, and used an external stylesheet to define CSS rules.

     

    I then downloaded and added into my project a cool, free “Cloud Control” that Rama Krishna Vavilala built and published (with full source-code) in a nice article here.  It encapsulates all of the functionality needed to render a list of weighted cloud tags within an ASP.NET page.  It also supports standard ASP.NET databinding – which means I can easily bind a result from a LINQ query to it to output the correct weighted tags for our application.

     

    My final Site.Master template to accomplish this ended up looking like this:

     

    <%@ Master Language="C#" AutoEventWireup="true" CodeFile="Site.master.cs" Inherits="Site" %>

    <%@ Register Namespace="VRK.Controls" TagPrefix="vrk" Assembly="VRK.Controls" %>

     

    <html xmlns="http://www.w3.org/1999/xhtml" >

        <head runat="server">

        <link href="StyleSheet.css" rel="stylesheet" type="text/css" />

    </head>

     

    <body>

        <form id="form1" runat="server">

     

            <div class="header">

                <h1>Scott's Photo Sample</h1>

            </div>

           

            <div class="tagcloud">

     

                <div class="subheader">

                    <h2>Filter Tags</h2>

                </div>

           

                <vrk:Cloud ID="Cloud1" runat="server"

                           DataTextField="Name"

                           DataTitleField="Weight"

                           DataHRefField="Name"

                           DataHrefFormatString="PhotoListing.aspx?tag={0}"

                           DataTitleFormatString="{0} photos"

                           DataWeightField="Weight" >

     

                </vrk:Cloud>

                   

            </div>

     

            <asp:contentplaceholder id="MainContent" runat="server">

            </asp:contentplaceholder>

     

        </form>

    </body>

    </html>

     

    The below Site.Master code-behind file is then used to obtain a unique list of tags from the database (it uses the “data-projection” feature of LINQ to fetch a sequence of custom shaped types containing the Tag name and usage count).  It then databinds this sequence to Rama’s control above like so:

     

    using System;

    using System.Query;

    using PhotoAlbum;

     

    public partial class Site : System.Web.UI.MasterPage {

     

        void PopulateTagCloud() {

     

            PhotoDB photoDb = new PhotoDB();

     

            Cloud1.DataSource = from tag in photoDb.Tags

                                group tag by tag.Name into g

                                orderby g.Key

                                select new {

                                    Name = g.Key,

                                    Weight = g.Count()

                                };

     

            Cloud1.DataBind();

        }

     

        protected void Page_Load(object sender, EventArgs e) {

            PopulateTagCloud();

        }

    }

     

    And now if I create an empty page based on the Site.Master above and hit it, I’ll automatically have the weighted tag-cloud added to the left-hand side of it:

     

     

    Step 5: Browsing Photos By Tag

     

    The next page I added to the site was one named “PhotoListing.aspx”.  The tag-cloud control used above creates a hyperlink for each tag that links to this page and passes the tag name as an argument to it when you click a tag.

     

    The PhotoListing.aspx page I created is based on the Site.Master template above and uses a templated DataList control to output the pictures in a two-column layout:

     

    <asp:Content ID="C1" ContentPlaceHolderID="MainContent" Runat="server">

           

        <div class="photolisting">

     

            <asp:DataList ID="PhotoList" RepeatColumns="2" runat="server">       

                <ItemTemplate>

                   

                    <div class="photo">

                      

                        <div class="photoframe">

                            <a href='PhotoDetail.aspx?photoid=<%# Eval("PhotoId") %>'>

                                <img src='<%# Eval("Thumbnail") %>' />     

                            </a>

                        </div>

                               

                        <span class="description">

                            <%# Eval("Description") %>

                        </span>   

                               

                    </div>           

                                   

                </ItemTemplate>

            </asp:DataList>

     

        </div>

     

    </asp:Content>

     

    Below is the entire code-behind for the page:

     

    using System;

    using System.Query;

    using PhotoAlbum;

     

    public partial class PhotoListing : System.Web.UI.Page  {

     

        void PopulatePhotoList(string tagName) {

     

            PhotoDB photoDb = new PhotoDB();

     

            if (tagName == null) {

                PhotoList.DataSource = photoDb.Photos;

            }

            else {

     

                PhotoList.DataSource = from photo in photoDb.Photos

                                       where photo.Tags.Any(t => t.Name == tagName)

                                       select photo;

            }

     

            PhotoList.DataBind();

        }

     

        protected void Page_Load(object sender, EventArgs e) {

            PopulatePhotoList( Request.QueryString["tag"] );

        }

    }

     

    When a user clicks on the “Cats” tag in the tag-cloud, they’ll then see this list of photos rendered:

     

     

    Step 6: Photo Details and Ajax Editing

     

    The PhotoListing.aspx page above links each thumbnail image to a PhotoDetails.aspx page that shows the picture full-size, as well as lists out all of its tags.  Users visiting the site can also optionally edit the tags using an inline Ajax-editing UI experience.

     

     

     

    To implement the Ajax-UI I used the Atlas UpdatePanel control, and then nested an ASP.NET MultiView control within it.  The Multiview control is a built-in control introduced in ASP.NET 2.0, and allows you to provide multiple “view” containers that can contain any HTML + server controls you want.  You can then dynamically switch between the views within your code-behind page however you want.  If the Multi-View control is nested within an Atlas UpdatePanel, then these view-switches will happen via Ajax callbacks (so no full-page refresh).

     

    For the tag editing experience above I defined both “readView” and “editView” views within the Multiview control, and added “edit”, “cancel” and “save” link-buttons within them like so:

     

    <atlas:UpdatePanel ID="p1" runat="server">

     

        <ContentTemplate>

                   

            <asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">

           

                <asp:View ID="readView" runat="server">

               

                    Tags:

                    <asp:Label id="lblTags" runat="server" />

                   

                    <span class="photoBtn">

                        <asp:LinkButton ID="btnEdit" runat="server" OnClick="btnEdit_Click">[Edit Tags]</asp:LinkButton>

                    </span>

                   

                </asp:View>

           

                <asp:View ID="editView" runat="server">

               

                    Tags:

                    <asp:TextBox ID="txtTags" runat="server" />

                   

                    <span class="photoBtn">

                        <asp:LinkButton ID="btnSave" runat="server" OnClick="btnSave_Click">[Save Tags]</asp:LinkButton>

                        <asp:LinkButton ID="LinkButton1" runat="server" OnClick="btnCancel_Click">[Cancel]</asp:LinkButton>

                    </span>

               

                </asp:View>

           

            </asp:MultiView>

     

        </ContentTemplate>

     

    </atlas:UpdatePanel>

     

    I then wired-up event-handlers for these 3 link-buttons in my code-behind like so:

     

        protected void btnEdit_Click(object sender, EventArgs e) {

            MultiView1.SetActiveView(editView);

        }

     

        protected void btnCancel_Click(object sender, EventArgs e) {

            MultiView1.SetActiveView(readView);

        }

     

        protected void btnSave_Click(object sender, EventArgs e) {

            UpdatePhoto(txtTags.Text);

        }

     

    The “save” event-handler above in turn calls the UpdatePhoto method and passes in the editView’s <asp:textbox> value as arguments.  This method is defined within the code-behind like so:

     

        void UpdatePhoto(string tagString) {

     

            PhotoDB photoDb = new PhotoDB();

            Photo photo = photoDb.Photos.Single(p => p.PhotoId == photoId);

     

            photoDb.Tags.RemoveAll(photo.Tags);

            photo.Tags = photoDb.TagWith(tagString, ' ');

     

            photoDb.SubmitChanges();

        }

     

    The above method retrieves the specified Photo from the database, removes its current tags, and then uses the below “TagWith” helper method to create a new collection of tag instances to associate with the picture:

     

        public EntitySet<Tag> TagWith (string tagNames, char separator) {

     

            EntitySet<Tag> tags = new EntitySet<Tag>();

     

            tagNames = tagNames.Trim();

     

            foreach (string tagName in tagNames.Split(separator))

               tags.Add(new Tag { Name = tagName });

     

            return tags;

       }

     

    And with that I now have an editable Ajax-enabled editing experience for viewing and dynamically adding new Tags to my photos. 

     

    Summary

     

    Hopefully the above post provides a good walkthrough of some of the really cool things you can do with LINQ, LINQ for SQL, ASP.NET 2.0 and Atlas.

     

    You can download the completed sample here.  Please review the “readme.txt” file in the root of the project to learn how to set it up and run it.

     

    To learn more about using LINQ and LINQ for SQL with ASP.NET 2.0, please review these past three blog posts of mine as well:

     

    Using LINQ with ASP.NET

    Using DLINQ with ASP.NET

    Using DLINQ with Stored Procedures

     

    Also make sure to check out the LINQ web-site here to download LINQ and start learning more about it (note: you need to install the May LINQ CTP build to run the sample).

     

    Last but most importantly: I want to say a huge thank-you to Anders Hejlsberg and Matt Warren – who are not only responsible for the insanely cool technology in LINQ + DLINQ, but also very kindly spent some of their valuable time over the last few days educating me on how to best approach the app above, and in making some invaluable coding suggestions. 

     

    Hope this helps,

     

    Scott

     

     

     

  • Tip/Trick: Fast HTML Editor Navigation within VS 2005

     

    Problem

     

    You have a large ASP.NET page that you are working on within Visual Studio, and you want to quickly navigate back and forth between design-view and source-view without getting lost in the deeply nested HTML content, or have to spend a lot of time finding your place again in the document.

     

    Solution

     

    Visual Web Developer and Visual Studio 2005 provide a number of subtle, but useful, features that you can take advantage of to navigate quickly around an HTML page.  To see them in action, open a page with a lot of HTML content (in the examples below I’m going to use the Default.aspx page from the built-in “Personal Starter Kit” sample). 

     

    Switch the page into design-view and either select some content, or simply move the cursor to a location within the page where you want to examine the HTML.  For example, in the screen-shot below notice that I’ve selected the “ipsum” text within the second <li> of my <ul> list:

     

     

    Now click the “source” tab within Visual Studio and notice the text selection and cursor within source view:

     

     

    As you can see above, VS has automatically highlighted and scrolled to the HTML that corresponds to the selected content within design-view, and positioned the cursor in the exact same region.  You can now type or start to cursor around within the editor without having to scan up and down the document to find the specific place you wanted to work on.

     

    You can use the same trick when moving from source->design-view.  In fact, if you position your cursor in HTML source-view on an ASP.NET Server Control element, and then switch into design-view, the control will be selected within the WYSIWYG designer and the property grid presented for that control.  For example, notice how the cursor is on the <asp:formview> control below:

     

     

    When we flip back into design-view it will be automatically selected within the designer and the formview will show up in the property grid:

     

     

    One last helpful navigation feature to make sure to take advantage of is the “tag navigator” control that shows up at the bottom of the HTML editor in both design-view and source-view:

     

     

    This lists the element hierarchy from the current location of the cursor up to the root <html> element in the document.  You can use this navigator to quickly see how the current element or cursor position is nested within the page and what CSS rules apply to it.  For example, in the picture above the cursor is within a <h4> element, which is within a <div> which has an id value of “content” (this is indicated by the “#” character above), which is within a <div> which has an id of “home” and a css class rule called “page” (which is indicated by the “.” character above), etc. 

     

    In addition to displaying the hierarchy of parent elements, you can also use the tag navigator to quickly navigate/select any of the parent elements.  Just click on the parent element name within the navigator and it will select it.  Right-click on an element and get even more control – allowing you to select everything (including the element itself), or just its inner contents: 

     

     

    This works in both source and design-view.

     

    Additional Reading

    Hope this helps,

     

    Scott

     

  • Tip/Trick: Using Server Side Comments with ASP.NET 2.0

     

    Problem

     

    You are coding away on an ASP.NET page, and are trying to isolate a problem within the page.  You have some existing html/controls/markup/in-line code that is being used on the page, and you want to temporarily comment it out while you fix the problem.

     

    Solution

     

    ASP.NET supports a little known feature called “server-side comments” that you can use to completely disable code/controls/html in a page.  Server-side comments in ASP.NET are delimited using a <%-- --%> syntax.  For example:

     

            <%--

                Commented out HTML/CODE/Markup.  Anything with

                this block will not be parsed/handled by ASP.NET.

           

                <asp:Calendar runat="server"></asp:Calendar> 

     

                <%# Eval(“SomeProperty”) %>     

            --%>

     

    One common question people ask is what the difference is between using client-side HTML comments and server-side comments.  The key difference is that with client-side comments it is the browser which is ignoring the content within them.  Code/controls within client-side comments will still be executed on the server and sent down to the browser.  As such, if there is a server error caused within them it will block running the page.

     

    With server-side comments, the ASP.NET compiler ignores everything within these blocks at parse/compile time, and removes the content completely when assembling the page (like its contents weren’t there at all).  Consequently, any errors caused by mal-formed controls or issues with inline code or data-binding expressions within them will be ignored.  The page is also just as fast with controls/code within server-side comments as if there were no controls/code on the page at all (there is no runtime performance overhead to them).

     

    One tip/trick to take advantage of in the HTML source editor within Visual Web Developer and VS 2005 is the automatic comment/uncomment feature they support.  You can use this by selecting some markup within a .aspx page, and then clicking the “comment” command button that is on HTML Source Editor Toolbar:

     

     

    This will automatically wrap the selected content with a <%-- --%> block.  You can likewise move the cursor within it and click the uncomment command to remove the comment.  Keyboard short-cuts are also automatically enabled to do this purely from the keyboard.  The exact keystrokes depend on what VS profile you have configured – but on my system it is “Ctrl-K, Ctrl-C” to comment a block and “Ctrl-K, Ctrl-U” to uncomment one.

     

    Note that this comment/uncomment command in VS works not only in HTML – but also within regular VB/C# source files as well.  It provides an easy and consistent way to comment out functionality everywhere within your project.

     

    Hope this helps,

     

    Scott

     

  • Tip/Trick: Custom formatting HTML in Visual Web Developer and Visual Studio 2005

     

    Problem

     

    You receive an HTML or ASP.NET page from another developer you work with whose html source formatting standards are different from your own (bad use of casing, inconsistent indenting, etc).

     

    You want to quickly clean-up and format the HTML “the right way” – where “the right way” is naturally defined as own your personal preference (and naturally every developer thinks their preference is "right" <g>).

     

    Solution

     

    Visual Web Developer and Visual Studio 2005 include a rich HTML source formatting rules engine that enables you to configure exactly how you want HTML markup to look.

     

    These HTML formatting rules are used by default whenever you add HTML elements or ASP.NET Server Controls within the WYSIWYG designer using the Toolbox.  You can also use these rules in source-view to select HTML you have imported into your project, and quickly format it with them. 

     

    Simply highlight the region of HTML you want to apply the source-formatting rules to in the source editor, and then right-click and choose the “Format Selection” context menu item:

     

     

    This will apply the HTML source formatting rules currently configured and clean up the HTML markup for you, without changing any rendering semantics:

     

     

    You can easily customize the HTML source formatting rules to match your own particular preference by right-clicking within the html source editor and choosing the “Formatting and Validation” context menu option:

     

     

    This will bring up a dialog that allows you to control the default capitalization and casing rules, attribute quoting, and self-terminating semantics of elements.  It also allows you to control when elements wrap to a new line within the page:

     

     

    By default if your line is more than 80 characters in length, it will wrap the text onto a new line (for example: if you have a GridView with a lot of properties you might see this happen).  If you prefer, you can either uncheck this behavior or increase/decrease the character length to change the wrapping semantics.

     

    You can also click the “Tag Specific Options” button to bring up a dialog that enables further element customization:

     

     

    Specifically, it allows you to control the colorization, line breaks, and default tag termination semantics for every client-side HTML element and ASP.NET server control.  For example, if I wanted to change the line break semantics of <div> elements so that their inner content region didn’t have line breaks, I could do so like this (note the preview pane that shows you how the setting will affect things):

     

     

    The per-tag colorization and bolding rules are useful if you want to specifically highlight either the foreground or background of important elements to make them stand-out more in the editor.  One scenario you might want to use this would be to highlight in red any “non-CSS recommended” element (like <tables>) to make you more aware when you are using them.

     

    Note that if you don’t want to tweak individual elements, you can click the “Default Settings” treeview node on the left to change the defaults for elements globally (note: you could then go in and override individual elements on a per element basis like above if you want).

     

    The VS source-formatter is smart and will correctly handle white-space rendering semantics for individual HTML elements.  For example: if you have a <a></a> or <td></td> elements it will be careful not to change any line-break or spacing semantics of the markup within it that would cause spacing because the rendering semantics changed.  If you format a selection of markup and see that a close tag hasn’t been moved to a separate line – it is because there is no space between the end of the preceding markup and the terminating tag, and as such VS is being careful not to change it to avoid changing the rendering semantics.

     

    The HTML source formatting rules you’ve set will be used anytime the WYSIWYG designer adds new HTML elements to a page (for example: you drag/drop an element from the toolbox).  The VS 2005 WYSIWYG designer will not re-format any customer HTML changes you’ve made in source view yourself.  Unlike previous versions of VS, it does not modify or change any markup you’ve typed as you switch back and forth between design view and source view, or change content within the design surface.  The WYSIWYG designer only uses the source formatting rules when you add new elements from the toolbox.

     

    Once you have the rules setup the way you want, you can also export and import them to other machines using the “Tools->Import and Export Settings” menu option.  This allows you to share your HTML source formatting rules either on the web or with other developers on your team.

     

    Additional Links

     

    Hope this helps,

     

    Scott

     

  • Tip/Trick: Gathering Custom User Registration Information

    Problem

    You are building a web-site that allows customers to register new users on the site.  As part of the registration process, you naturally want to allow them to create a username and password.  You also want them to enter additional personalization/registration data like their address, zipcode, gender, age, etc. and associate it with the newly created account.  You want to create an intuitive wizard-like navigation UI that enables customers to easily manage this workflow.

    Solution

    ASP.NET 2.0 now provides a built-in control -- <asp:createuserwizard> -- that provides developers with an easy way to create user registration workflows for their site.  The <asp:createuserwizard> control provides built-in UI to enable an end-user to choose a username and password.  The <asp:createuserwizard> control also allows developers to specify additional "custom steps" of information to gather.   

    These custom steps can be defined using additional wizardstep templates defined within the <asp:createuserwizard> control itself -- and so can contain any custom UI the developer wants.  For example:

    <asp:WizardStep ID="CreateUserWizardStep0" runat="server">
        <div class="title">
            Billing Information
        </div>
        <div class="address">
           <span>Billing Address:</span>
           <asp:TextBox ID="BillingAddress" MaxLength="50" runat="server"  />
           <asp:RequiredFieldValidator ControlToValidate="BillingAddress" ErrorMessage="Address required!" runat="server"/>
        </div>
    </asp:WizardStep>

    The CreateUserWizard control will then automatically add "next"/"previous" navigation UI to enable an end-user to skip forward and back throughout the registration process.

    The CreateUserWizard control exposes a "CreatedUser" event that you can handle within your page to add logic to retrieve the information collected within the various WizardSteps, and store it within whatever database or profile store you want.  This event fires after the user has been created within the ASP.NET Membership system.  However, the user is not yet logged into the ASP.NET site at this point (they won't be logged in until the page redisplays).  So to obtain the username of the newly created username you should access the CreateUserWizard.UserName property.

    For example:

    Sub CreateUserWizard1_CreatedUser(Sender as Object, E as EventArgs) Handles CreateUserWizard.CreatedUser

       ' Obtain a reference to the "BillingAddress" textbox in the first step of the wizard

       Dim BillingAddress as TextBox

       BillingAddress = CreateUserWizardStep0.ContentTemplateContainer.FindControl("BillingAddress")

     

      ' Todo: Store the BillingAddress.Text value in a database or profile store

    End Sub

    Two possible places you could perist this custom user information are:

    1) Within a custom database table that you create and define.  You could replace the above "todo" statement in the CreatedUser event handler to insert this data into the database table using whatever data API you prefer (for example: ADO.NET or a Strongly Typed Table Adapter). 

    2) Within the new ASP.NET 2.0 Profile system.  The ASP.NET Profile system provides a way to automatically persist additional properties/values about a user in a persistent store, and provides a strongly typed API that enables you to easly set/retrieve them (so for example you could just write Profile.BillingAddress to access the value).  The ASP.NET Profile system is by default mapped against an XML blob-like column within a database (which makes it easy to setup).  Alternatively, you can use the ASP.NET SQL Table Profile Provider to map the Profile API against a schematized SQL table or set of SPROCs.  This gives you the nice strongly typed Profile API against a regular SQL table (which makes data-mining easier).

    To learn more about how to use the <asp:createuserwizard> control and download sample code that uses it I'd recommend reviewing these articles:

    Customizing the CreateUserWizard Control: This article was published in July of 2006, and provides a good walkthrough of the customization capabilities of the CreateUserWizard control, and how to store custom user properties directly within a database.

    How to add a Login, Roles, and Profile system to an ASP.NET 2.0 app in 24 lines of code: This is a sample I put together towards the end of 2005 that shows how to integrate these three features in ASP.NET 2.0 to create a custom registration and profile management system.

    CreateUserWizard Samples within the ASP.NET QuickStart tutorials: This page provides a number of samples (in both VB and C#) that you can use to understand how to use the CreateUserWizard control better.

    Profiles in ASP.NET 2.0: K. Scott Allen posted this nice article which does a good job providing an overview of the new ASP.NET 2.0 Profile API.

    ASP.NET 2.0 Membership, Roles, Forms Authentication and Security Resources: This blog post of mine contains a ton of ASP.NET security informationa and useful links.  I'd recommend reviewing it to explore more about ASP.NET security.

    Credits

    Erich Peterson and K. Scott Allen for the nice articles I listed above.

    Hope this helps,

    Scott

  • Blog Comments and RSS Subscriptions to Them

    One of the things that I recently enabled for my blog was RSS feeds for individual post comments (I think I accidentally forgot to enable this when I switched over to the new Community Server blog software).  This enables you to subscribe to the discussions around individual blog postings without having to bookmark or revist the post manually.  Just look for the "Subscribe to this post's comments using RSS" link above the "Comments" section on a post to obtain the feed URL to subscribe.

    One question I sometimes get asked is "why does it take awhile for my comments to show up on your blog?"  Unfortunately due to a lot of spam, I've had to enable moderation on the comments which contributes to the delay (note: I'm hoping to find a way to enable a CAPTCHA check to eliminate the SPAM and do away with moderation entirely). 

    The other reason for the delay is because I tend to get a lot of comments and individual emails each day (about 80 per day from customers), and depending on my work/travel-load it can take me a few days to get back to all of them.  I find that if I approve a comment question without answering it at the same time, I end up forgetting to come back to it.  So sometimes I'll hold off on approving it so that it stays on my todo list -- and then I'll approve and answer it at the same time.  If you subscribe to the RSS feed of a post you've asked a question on, this should help you with getting notified that an answer is ready.

    Hope this helps,

    Scott

    P.S. To the 70 people who posted comments yesterday (which was the July 4th holiday in the US), your comment questions have now all been answered. ;-)

     

  • New Atlas CTP Posted (now includes updatepanel support in dynamic controls)

    Last week we released the June CTP build of Atlas.  You can download it now from the http://atlas.asp.net site.

    In addition to bug-fixes, the big new feature provided with this Atlas release is support for dynamically adding UpdatePanel controls within an ASP.NET page.  What this means is that you can now embed UpdatePanels within DataLists, within WebParts, within any template of a control, and within other dynamically created composite controls.  This greatly expands the places that UpdatePanels can easily be used, and enables a ton of new scenarios.

    <asp:DataList runat="server">
      <ItemTemplate>
        <asp:UpdatePanel runat="server">
          ...

        </asp:UpdatePanel>
      </ItemTemplate>
    </asp:DataList>

    Control developers can also now use UpdatePanels when building composite controls.  Nikhil has a nice post that describes how a control developer can use updatepanels within composite controls to enable Ajax scenarios within portions of a control.  You can read his post on how to-do this here.

    Three new controls were also added to the Atlas Control Toolkit last week.  These include a nice "Accordian" control to enable collapsable multi-regions, a "NumericUpDown" control to easily increment/decrement numeric values, and a "ResizableControl" to enable user resizing of any control element.  You can download the source code to these controls and start using them now here.  Note that the current download as of this weekend is still using the April CTP of Atlas -- the Atlas Control Toolkit team will be updating the drop to use the June CTP later this week.

    For additional information on Atlas also make sure to checkout this recent Atlas resources list I posted here.

    Hope this helps,

    Scott

     

  • Tip/Trick: List Running ASP.NET Worker Processes and Kill/Restart them from the command-line

    Problem

    You want a quick way to kill a process on your system, or kill and restart an ASP.NET or IIS worker process. 

    Solution

    Windows has two built-in command-line utilities that you can use to help with this: Tasklist and Taskkill

    Within a command-line window you can type "Tasklist" to obtain a listing of all of running Windows processes on your system:

    C:\Documents and Settings\Scott>tasklist

    Image Name                   PID Session Name     Session#    Mem Usage
    ========================= ====== ================ ======== ============
    System Idle Process            0 Console                 0         16 K
    System                         4 Console                 0        212 K
    smss.exe                     824 Console                 0        372 K
    csrss.exe                    876 Console                 0      5,116 K
    winlogon.exe                 900 Console                 0      3,848 K
    services.exe                 944 Console                 0      4,112 K
    lsass.exe                    956 Console                 0      1,772 K
    svchost.exe                 1372 Console                 0     22,240 K
    svchost.exe                 1524 Console                 0      3,428 K
    svchost.exe                 1572 Console                 0      4,916 K
    spoolsv.exe                 1788 Console                 0      5,660 K
    inetinfo.exe                 352 Console                 0      9,860 K
    sqlservr.exe                 612 Console                 0      7,348 K
    sqlservr.exe                 752 Console                 0     15,552 K
    explorer.exe                2960 Console                 0     25,224 K
    CTHELPER.EXE                3660 Console                 0      4,964 K
    LVComS.exe                   872 Console                 0      3,092 K
    msmsgs.exe                  3596 Console                 0      6,532 K
    sqlmangr.exe                3096 Console                 0      4,264 K
    OUTLOOK.EXE                 1740 Console                 0     75,992 K
    iexplore.exe                 472 Console                 0     37,372 K
    cmd.exe                      732 Console                 0      2,436 K
    tasklist.exe                3104 Console                 0      4,156 K
    wmiprvse.exe                3776 Console                 0      5,416 K

    TaskKill can then be used to terminate any process instance in the above list.  Simply provide it with the PID (Process ID) value of the process instance to kill and it will terminate it:

    C:\Documents and Settings\Scott>taskkill /pid 1980
    SUCCESS: The process with PID 1980 has been terminated.

    ASP.NET on Windows 2000 and XP runs code within the "aspnet_wp.exe" worker process (when using IIS).  On Windows 2003 it runs within the IIS6 "w3wp.exe" worker process.  Both of these processes are launched from Windows system services, which means you must provide the "/F" switch to taskkill to force-terminate them:

    C:\Documents and Settings\Scott>tasklist

    Image Name                   PID Session Name     Session#    Mem Usage
    ========================= ====== ================ ======== ============
    aspnet_wp.exe               3820 Console                 0     13,512 K

    C:\Documents and Settings\Scott>taskkill /pid 3820 /F
    SUCCESS: The process with PID 3820 has been terminated.

    As a short-cut, you can also just provide the process image name to "Taskkill" if you want to avoid having to lookup the PID value for a specific process instance.  For example, the below command will kill all ASP.NET worker processes on the system:

    C:\Documents and Settings\Scott>taskkill /IM aspnet_wp.exe /F
    SUCCESS: The process "aspnet_wp.exe" with PID 2152 has been terminated.

    ASP.NET and IIS will automatically launch a new worker process the next time a request is received by the system.  So when you run the above command it will shutdown all active ASP.NET Worker processes.  When you then hit the site again a new one will be automaticlaly launched for it (and it will have a new PID as a result).

    Note that both TaskList and TaskKill support a "/S" switch that allows you to specify a remote system to run the commands against.  If you have remote admin rights on one of your co-workers machines this can be a lot of fun.

    Credits

    The above tutorial was inspired by Steve Lamb's nice post here.  I was surprised to discover that these commands work with Windows XP and Windows 2003 too.  Since the mid-90s I've been using a private set of utilities I always install on my dev box to accomplish the same task, without realizing that somewhere along the way they've been built-into Windows.  Many thanks to Steve for pointing them out.

    Hope this helps,

    Scott