Blogs

Paul Kimmel's Blog

March 2009 - Posts

  • Implementing an IHttpModule

         

    IHttpModule is an interface that contains two methods to implement: Init and Dispose. What is cool and useful about IHttpModules is that you register classes that implement this interface in the Web.Config file, and the code is called for every single HTTP call whether it is a postback—regular round trip to the server—or a callback—typically associated with asynchronous Ajax calls. In short, IHttpModule exists to let you insert code into every HTTP call in the pipeline.

    To define an IHttpModule derive a class that implements the IHttpModule interface and register the module containing the class in the Web.Config in the <httpModules> section. What you elect to do in the module is up to you. You can catch Application errors in one place if you want to for instance, or you could hook into other Application events and handler other kinds of operations. Listing 1 shows an implementation of IHttpModule, and Listing 2 shows how to load the module in the Web.Config.

    Listing 1: Custom IHttpModule that intercepts every HTTP request and in this case only tracks errors.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Diagnostics;

    public class MyCustomModule : IHttpModule
    {
        public MyCustomModule()
        {
        }

      #region IHttpModule Members

      public void Dispose()
      {
      }

      public void Init(HttpApplication context)
      {
        context.Error += new EventHandler(context_Error);
      }

      void context_Error(object sender, EventArgs e)
      {
        try
        {
          HttpApplication application = (HttpApplication)sender;
          Exception exception = application.Server.GetLastError();

          // this would clear all exceptions -- which is interesting to see
          // but a bad idea in practice
          //application.Server.ClearError();

          EventLog.WriteEntry("Application", exception.Message);

        }
        catch { }
      }

      #endregion
    }

    Listing 2: The custom IHtpModule is shown in bold font. (Some of the Web.Config items were stripped out by me to shorten the listing.)

    <?xml version="1.0"?>
    <!--
        Note: As an alternative to hand editing this file you can use the
        web admin tool to configure settings for your application. Use
        the Website->Asp.Net Configuration option in Visual Studio.
        A full list of settings and comments can be found in
        machine.config.comments usually located in
        \Windows\Microsoft.Net\Framework\v2.x\Config
    -->
    <configuration>

        <configSections>
        </configSections> 

        <appSettings/>
        <connectionStrings/>
        <system.web>
            <!--
                Set compilation debug="true" to insert debugging
                symbols into the compiled page. Because this
                affects performance, set this value to true only
                during development.
            -->
            <compilation debug="false">

              <assemblies>
              </assemblies>

            </compilation>
            <!--
                The <authentication> section enables configuration
                of the security authentication mode used by
                ASP.NET to identify an incoming user.
            -->
            <authentication mode="Windows" />
            <!--
                The <customErrors> section enables configuration
                of what to do if/when an unhandled error occurs
                during the execution of a request. Specifically,
                it enables developers to configure html error pages
                to be displayed in place of a error stack trace.

            <customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
                <error statusCode="403" redirect="NoAccess.htm" />
                <error statusCode="404" redirect="FileNotFound.htm" />
            </customErrors>
            -->

          <pages>
          </pages>

          <httpHandlers>
          </httpHandlers>
          <httpModules>
            <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
            <add type="MyCustomModule" name="MyCustomModule" />
          </httpModules>
        </system.web>

        <system.codedom>
          <compilers>
          </compilers>
        </system.codedom>
        <!--
            The system.webServer section is required for running ASP.NET AJAX under Internet
            Information Services 7.0.  It is not necessary for previous version of IIS.
        -->
        <system.webServer>
          </handlers>
        </system.webServer>

        <runtime>
          <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <dependentAssembly>
              <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
              <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
            </dependentAssembly>
            <dependentAssembly>
              <assemblyIdentity name="System.Web.Extensions.Design" publicKeyToken="31bf3856ad364e35"/>
              <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="3.5.0.0"/>
            </dependentAssembly>
          </assemblyBinding>
        </runtime>

    </configuration>

    In Listing 1 the IHttpModule writes every exception to the EventLog. Clearly you could implement basic logging in the Global.asax file’s Application_Error, which is equivalent to what this IHttpModule is doing. Of course, even implementing an error logging handler is more portable because you could put it in a redistributable assembly and write once and use many times.

    An interesting use of the IHttpModule is Developer Express’ ASPxHttpHandlerModule. The ASPxHttpHandlerModule works especially well with our Ajax-capable controls. Generally, Ajax calls don’t call a Web page’s Render part of the lifecycle, thus if an error occurs in an Ajax call an application appears to hang. If you register the ASPxHttpHandlerModule then you can choose between the default behavior of Ajax errors being sent to the client or page redirection. The ASPxHttpHandlerModule can be added to a project from the Smart tags menu of our controls.

  • Declarative Programming

         

    Declarative programming is all the rage these days (along with functional programming). What is declarative programming? Declarative programming is when you specify the "what" without the "how". Declarative programming is about statements of work--get some records--without specifying the algorithm or logic that indicates how the work is to be performed.

    Declarative programming shows up in places like .aspx pages for something like the SqlDataSource. You might see DeleteCommand-"DELETE FROM Customer WHERE CustomerID = @CustomerID". That's it. The framework knows how to take the declarative delete statement and pile on the ADO.NET plumbing. Old timers like me--20 plus years--sometimes don't like declarative programming because we have gotten used to writing the plumbing and using code generators, snippets, and CodeRush (tm) to speed up writing this code. In general though declarative programming saves you time and consequently money simply because writing plumbing code even with a tool like CodeRush takes time, has to be debugged, and can still be a little error prone.

    For straightforward pages with something like our ASPxGridView control use declarative programming when it is available. The SQL statements and the declarative bits are not served to the client, so the old worries about exposing schema information or mitigated. You can always step back and write logic if you have to, but you can never get that time back if you start with imperative--old school how and what--code.

    ...

    I will be at the Central Ohio Day of .NET in Columbus on April 18th representing Developer Express. Stop by our booth and let me know if you have read the blog.

  • Using the Filter Grouping Feature of the ASPxGridView

         

    The first time I looked at the ASPxGridView it was a little overwhelming to look at the sheer number of properties this one Web control had. I am not easily overwhelmed, but if memorization of properties and sub-properties were an objective, my not so rubbery old brain might have grappled with the task. However, since no one require that you or I memorize "stuff" the ASPxGridView is a powerful grid. I think of Developer Express' grid in two ways: its a grid, and when I need a grid its the one I want to us.

    Instead of memorize dozens of properties think of the ASPxGridView as a grid capable of sorting, searching, grouping, summarizing, nesting, and using Ajax for performance improvements. Then, when you want to perform a task focus on the properties and sub-properties associated with the task at hand. This approach to capabilities makes the ASPxGridView seem much more manageable, less complex. Suppose for example you want to use the filtering capability of the filtering control and explain how filter groups work then focus on that information and "temporarily" tune out the other features. (Of course, don't permanently tune out the other features because those other features make our grid the grid to own.) Let's walk through using the filtering control and how the filter groups work.

    Enabling the Filter Bar/Control for the ASPxGridView

    With the ASPxGridView you can get a grid on a form, connect the grid to a data source, put data in it and enable filtering in about 7 steps and no lines of code (that you have to write). To wire up a grid with some data to explore filter groups follow these steps (Or, if you know how to get the basic grid plumbing wired up skip to the next section):

    1. Create a new Web application
    2. Add an ASPxGridView to the default <div> tag section of the Default.aspx page
    3. Click the grid's Task menu, click Choose Data Source, and pick <New data source> from the drop down
    4. In the Data Source Configuration Wizard choose Database as the source type to add a SqlDataSource
    5. Use the Northwind database (or any other you'd like), pick the Orders table, and complete the wizard
    6. Click on the ASPxGridView and press F4 to display the Properties window
    7. Expand the Settings property and change ShowFilterBar to Visible and ShowHeaderFilterButton to true

    Run the sample Web application. The results should look something like Figure 1 if you use the Northwind Orders table with the options listed above. To display the filter control click the Create Filter link shown in the lower left hand side of Figure 1.

    image
    Figure 1:  The ASPxGridView with settings to display the Filter Bar (bottom left link) and the header filter button (drop down button right of each column header).

    Defining Search Criteria Using the Filter Control

    Click the Create Filter link to display the Filter Builder control (see Figure 2). The Filter Builder supports visually building a search (or filter criteria) using a dynamic, model dialogue. By default the Filter Builder is empty containing the logical operator link and the (+) button dhow in Figure 2. Click the logical operator link to choose between And, Or, Not And, and Not Or, or to Add a Group or Condition or Remove conditions. Click the (+) button to add a condition to the group below the operator.

    image
    Figure 2: The Filter Builder control shown in the foreground.

    The logical operator defines how every predicate in the group is chained together. The predicates are the filter conditions: [Order ID] Is greater than 9000, etc. Each predicate added to the group becomes part of a compound predicate evaluated with the group's operator, in the example And. You can add addition sub-groups and pick a logical operator for the sub-group. If the sub-group uses the same operator as the parent group then the sub-group operator will not be repeated and the conditions (or predicates) will be elevated to parent group. The basic rules for organizing conditions/predicates is based roughly on discrete mathematics (or the propositional and predicate calculus).

    image
    Figure 3: A group withe And operator and two conditions/predicates.

    Think "Order ID Is greater than 9000" as predicate A, and "Employee ID Is greater than or equal to 5" as predicate B. The filter control thinks of the filter as "A And B", that is a logical-and operation between A and B. If you add additional conditions/predicates at this level they are and'd also. Suppose you add an Or group and a condition "Employee ID Equals 6" (which we will call predicate C). Any other conditions added to the Or group are Or'd with C, but these groups make up a compound group and the compound group (right now with only C in it, refer to Figure 4) is and'd to A and B. Thus the actual filter is "A AND B AND C". If you added D to the Or group then the filter would be (A AND B) AND (C OR D).

    image 
    Figure 4: Since there is only one condition after the Or the three test conditions are And'd together; the sub-group "Employee ID Equals 6" would be Or'd with any thing else in its sub-group, but all of the conditions would be And'd based on the outer  logical operator And.

    Logical operators only apply to items in the group whether they are singular or compound conditions. If you mean

    Employee ID equals 6 Or (Order ID Is greater than 9000 And Employee ID Is greater than or equal to 5)

    which would yield an Order ID for Employee 6 then create the Or group first and the And sub-group (see Figure 5).

    image
    Figure 5: The correct way to get Employee ID 6 for orders below 9000 is to create an Or condition for Employee ID 6 and And the sub-group with the restrictive conditions.

    The best part of all of this is that the grid and the filter builder use Ajax, so your users will be able to experiment with the filter builder quickly and easily to get the result set intended. Non-technical users may have trouble with logical operators period. For these uses consider using the header filter or header menu filters. The latter two filter-styles aren't as powerful, but to some extent it depends on your audience. Developer Express gives you the power to choose between filtering options without writing a lot of code.

  • Praise for Essential LINQ

         

    When Addison-Wesley asked me to write a blurb for Charlie Calvert and Dinesh Kulkarni's book Essential LINQ I was happy to do it. Although the book competes directly with my book LINQ Unleashed from Sams I know Charlie by reputation (and have spoken with him a few times). Charlie is great at explaining techniques and technologies and in his words is lucky enough to work with smart people who let him explain things. (One of those smart people being Anders Hejlsberg.)

    I'd like to think that you will get your money's worth from both books and the scholarship for both books offer independent perspectives and experiences, and I'd like to thank Microsoft. While Charlie and I frequent some of the same events (TechEd and what not), Microsoft has always made inside technical people available to non-employees in an effort to help you get the best information available.

    Why is LINQ relevant to DevExpress customers? The answer is that LINQ is a core technology for all programmers and there are useful benefits to LINQ for customers using our controls as well as Microsoft's standard controls.

    Happy St. Patrick's Day!

  • Using a SQL View with Persistent Classes (XPO)

         

    Developer Express' Persistent Classes wizard does not show views. It'd be cool if we could add views and table-valued functions. However, you can manually define a persistent class based on a view and XPO, the XpoDataSource, and controls like the ASPxGridView can use the view-based persistent class to display your data. There is a small quirk: if the view does not contain a single primary key then running the XpoDataSource in server mode--one of our coolest performance features--doesn't work correctly. (This is a noted limitation and there is a reason for it.) Before I show you how to create persistent class based on a view with and without a primary key (and toss in a couple of neat tricks you might like) let's look at the aforementioned limitation.

    image 
    Figure 1: Look ma no views or table-valued functions!

    Why XPO Needs Primary Keys

    The XpoDataSource has a property named ServerMode. ServerMode is important because when the XpoDataSource is in ServerMode it pushes user requests to the back-end database server instead of the DataController. The DataController runs on the web server and generally the SQL Server has (or can have) its own box. Why does this matter? Well, manipulating a huge DataSet like a sort operation or custom objects on the Web server can slow the Web server down. On the other hand SQL Server boxes tend to be beefy and are much better at doing things like sorting and grouping when compared to a separate DataController. Suppose for example you have the ASPxGridView with a SqlDataSource. You click a column header to sort. The sort has to sort all of the data in the result set regardless of whether it is all showed in the grid or not to make the sort meaningful. For the sort to work then the data needs to be in memory and sorted by some code in the .NET framework. Now suppose you have the ASPxGridView with the XpoDataSource with ServerMode = true. When you click on the column header the data is sorted by sending a query with and order by to the database; the work is offload to the database server alleviating pain on the web server and improving performance without loading all data in memory.

    The reason a primary key is needed is to support paging. With paging you have an implicit index into the result set based on the page. So when the XpoDataSource is in ServerMode the page and number of items per page act as a collection of indexes for the resultset and XPO has to send unique key references and sort order information to the SQL Server to get the right result set. Without uniqueness duplicate item resolution is impossible. The first listing (Listing 1) is one query sent when XPO is requesting data based on the Northwind.Products table with a sort order indicated. Listing 2 shows how the query is built when the ASPxGridView uses paging in conjunction with a specified sort order.

    Listing 1: The XpoDataSource in ServerMode with a sort specified (from an ASPxGridView).

    select N0."ProductID" from "dbo"."Current Product List" N0
    order by N0."ProductID" asc

    Listing 2: XpoDataSource in ServerMode sends a query to reflect the items needed relative to the page index, off loading data intensive work to the database.

    exec sp_executesql N'select N0."ProductID",N0."ProductName" from "dbo"."Current Product List" N0
    where N0."ProductID" in
    (@p0,@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p10,@p11,@p12,@p13,@p14,
    @p15,@p16,@p17,@p18,@p19,@p20,@p21,@p22,@p23,@p24,@p25,@p26,@p27,
    @p28,@p29,@p30,@p31,@p32,@p33,@p34,@p35,@p36,@p37,@p38,@p39,@p40,
    @p41,@p42,@p43,@p44,@p45,@p46,@p47,@p48,@p49,@p50,@p51,@p52,@p53,
    @p54,@p55,@p56,@p57,@p58,@p59,@p60,@p61,@p62,@p63,@p64,@p65,@p66,@p67,@p68,@p69)',N'@p0
    int,@p1 int,@p2 int,@p3 int,@p4 int,@p5 int,@p6 int,@p7 int,@p8 int,@p9 int,@p10 int,@p11 int,@p12 int,@p13 int,@p14 int,@p15 int,@p16
    int,@p17 int,@p18 int,@p19 int,@p20 int,@p21 int,@p22 int,@p23 int,@p24 int,@p25 int,@p26 int,@p27 int,@p28 int,@p29 int,@p30 int,@p31
    int,@p32 int,@p33 int,@p34 int,@p35 int,@p36 int,@p37 int,@p38 int,@p39 int,@p40 int,@p41 int,@p42 int,@p43 int,@p44 int,@p45 int,@p46
    int,@p47 int,@p48 int,@p49 int,@p50 int,@p51 int,@p52 int,@p53 int,@p54 int,@p55 int,@p56 int,@p57 int,@p58 int,@p59 int,@p60 int,@p61
    int,@p62 int,@p63 int,@p64 int,@p65 int,@p66 int,@p67 int,@p68 int,@p69
    int',@p0=25,@p1=26,@p2=27,@p3=30,@p4=31,@p5=32,@p6=33,@p7=34,@p8=35,
    @p9=36,@p10=37,@p11=38,@p12=39,@p13=40,@p14=41,@p15=43,
    @p16=44,@p17=45,@p18=46,@p19=47,@p20=48,@p21=49,@p22=50,@p23=51,
    @p24=52,@p25=54,@p26=55,@p27=56,@p28=57,@p29=58,@p30=59,@p31=60,
    @p32=61,@p33=62,@p34=63,@p35=64,@p36=65,@p37=66,@p38=67,@p39=68,
    @p40=69,@p41=70,@p42=71,@p43=72,@p44=73,@p45=74,@p46=75,@p47=76,
    @p48=77,@p49=78,@p50=23,@p51=22,@p52=21,@p53=20,@p54=19,@p55=18,
    @p56=16,@p57=15,@p58=14,@p59=13,@p60=12,@p61=11,@p62=10,@p63=8,@p64=7,@p65=6,@p66=4,@p67=3,@p68=2,@p69=1

    Using Persistent Classes with a View with a Primary Key

    If you want to use a view and it has a primary key then you can roll your own persistent class. The steps are as follows:

    1. Add an ASPxGridView to a Web page
    2. Add an XpoDataSource to a Web page and set ServerMode = true
    3. Add a class (see Listing 3) to your web project
    4. Add the startup code to a Global.asax file (which means you need to add a Global.asax file too)(see Listing 4)
    5. Add the Page_Init code to the default.aspx page (the one containing the ASPxGridView)
    6. Set the TypeName of the XpoDataSource to the name of your persistent class
    7. Run the demo

    Listing 3: Define a class that inherits from XPLiteObject that represents your underlying view.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using DevExpress.Xpo;

    [Persistent("Current Product List")]
    public class CurrentProductList : XPLiteObject
    {
      [Key, Persistent("ProductID")]
      public int ProductID;

      [Persistent("ProductName")]
      public string ProductName;

      public CurrentProductList(Session session) : base(session) { }
      public CurrentProductList() : base(Session.DefaultSession) { }
      public override void AfterConstruction() { base.AfterConstruction(); }
    }

    The class defines ProductID as the key and a persistent column. ProductName is a persistent column, and the PersistentAttribute on the class names the underlying view.

    Listing 4: Write the plumbing for the Xpo Persistent Class.

    <%@ Application Language="C#" %>

    <script runat="server">

        void Application_Start(object sender, EventArgs e)
        {
          string connectionString =
         DevExpress.Xpo.DB.MSSqlConnectionProvider
         .GetConnectionString(@".\SQLExpress",
           "northwind");

          DevExpress.Xpo.Metadata.XPDictionary dictionary =
            new DevExpress.Xpo.Metadata.ReflectionDictionary();

          DevExpress.Xpo.DB.IDataStore store =
            DevExpress.Xpo.XpoDefault.GetConnectionProvider(connectionString,
            DevExpress.Xpo.DB.AutoCreateOption.SchemaAlreadyExists);

          dictionary.GetDataStoreSchema(typeof(CurrentProductList).Assembly);
          DevExpress.Xpo.XpoDefault.DataLayer = new DevExpress.Xpo.ThreadSafeDataLayer(dictionary, store);
          DevExpress.Xpo.XpoDefault.Session = null;

        }
        void Application_End(object sender, EventArgs e)
        {
            //  Code that runs on application shutdown

        }
        void Application_Error(object sender, EventArgs e)
        {
            // Code that runs when an unhandled error occurs

        }

        void Session_Start(object sender, EventArgs e)
        {
            // Code that runs when a new session is started

        }

        void Session_End(object sender, EventArgs e)
        {
            // Code that runs when a session ends.
            // Note: The Session_End event is raised only when the sessionstate mode
            // is set to InProc in the Web.config file. If session mode is set to StateServer
            // or SQLServer, the event is not raised.

        }
    </script>

    The XPO wire-up code is pretty boilerplate. Copy and paste this code to your toolbox or better yet create a CodeRush template for it. You can also copy the Page_Init to your toolbox (maybe create an XPO tab) or again, a CodeRush template. Listing 6 and Figure 2 show the Page_Init template and the CodeRush Options dialog, respectively. (pin<space> activates the template as defined.)

    Listing 5:  Associate a Session object with the XpoDataSource in the Page_Init method.

    protected void Page_Init(object sender, EventArgs e)
    {
      XpoDataSource1.Session = new DevExpress.Xpo.Session();
    }

    image 
    Figure 2: The CodeRush Options dialog shows the template  definition information the Page_Init session wire-up.

    Watching XPO in Action

    If you just think of XPO as another simple competitor in the Data Access Layer well you (are making the same mistake I did originally and) are missing two powerful features in addition to persistent class generation. XPO can generate the database from the classes as well as classes from the database, and XPO uses a server mode which takes advantage of the power of your database server by pushing client queries back to the database server using Ajax. This means the Web server gets a break and the optimized-for-the-job SQL server is pitching where it helps. Its cool to watch this back-end action.

    You can see XPO doing its thing by opening the SQL Profiler and selecting File|New Trace. Run the demo application and you will see the queries. Use grouping, sorting or paging and you will see that the data is actually being fetched at the database server level. Figure 3 shows the SQL Profiler responding to a group operation on the client.

    image
    Figure 3: Drag a column to the group panel  and with the magic of Ajax and the XpoDataSource ServerMode the grid is reconfigured and the data is grouped.

    Using XPO with Views without Primary Key Fields

    You can also use the XpoDataSource with views that don't have a unique key. Essentially what you have to do is construct a unique key out of other columns. Do this by defining an additional struct with the persistent fields and then define a variable of the struct type in the persistent class. Sorting won't work in ServerMode for the aforementioned reason but you will be able to see your view data in the ASPxGridView. If you want sorting then set the XpoDataSource.ServerMode=false. The necessary revision to the code is shown in Listing 6 and the output is shown in Figure 4.

    Listing 6: The revised code; use this approach if you have a view without a unique key in the resultset.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using DevExpress.Xpo;

    public struct CurrentProductListKey
    {
      [Persistent("ProductID")]
      public int ProductID;

      [Persistent("ProductName")]
      public string ProductName;

    }

    [Persistent("Current Product List")]
    public class CurrentProductList : XPLiteObject
    {
      [Key, Persistent]
      private CurrentProductListKey Key;

      public int ProductID { get { return Key.ProductID; } }
      public string ProductName { get { return Key.ProductName; } }
      public CurrentProductList(Session session) : base(session) { }
      public CurrentProductList() : base(Session.DefaultSession) { }
      public override void AfterConstruction() { base.AfterConstruction(); }
    }

    image
    Figure 4: The output from the Current Product List view.

    That's it. Be sure to check out Developer Express' booth at TechEd in Los Angeles, and if you want some demos on our controls on LA drop me a line, I will try to whip some up for you.

More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.