Bases de données
Développement Web
Gestion de projets
Programmation

Mise en place rapide d'un GridView - Brice FROMENTIN

Nous avons souvent à utiliser une GridView qui affiche une liste de lignes provenant directement de nos données préférées. Mais voilà, comme d’habitude nous cherchons à aller vite, alors qu'est ce qui se passe derrière ? Je vais ici n’utiliser que les Wizards pour comparer ce qui se passe derrière tout ça.

Pour ce test, j’utilise la base AdventureWorks sur SQL Server 2008 R2 et ASP.NET 4. Le but étant d’afficher la liste des contacts (Title, FirstName, MiddleName & LastName), d’avoir un pager et de pouvoir trier. Pour ceci, je vais utiliser soit un SqlDataSource ou un LinqDataSource ou un EntityDataSource.

SqlDataSource

On précise ici simplement notre SELECT et le SqlDataSource s’occupe de tout.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SqlDataSource.aspx.cs" Inherits="SqlDataSource" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

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

<head runat="server">

    <title>SqlDataSource</title>

</head>

<body>

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

    <asp:SqlDataSource ID="x_oSqlDataSource" runat="server" ConnectionString="<%$ ConnectionStrings:AdventureWorks %>"

        SelectCommand="SELECT Title, FirstName, MiddleName, LastName FROM Person.Contact">

    </asp:SqlDataSource>

    <div>

        <asp:GridView ID="x_grdContacts" runat="server" AllowPaging="True" AllowSorting="True"

            AutoGenerateColumns="False" CellPadding="4" DataSourceID="x_oSqlDataSource" ForeColor="#333333"

            GridLines="None">

            <AlternatingRowStyle BackColor="White" ForeColor="#284775" />

            <Columns>

                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />

                <asp:BoundField DataField="FirstName" HeaderText="FirstName" SortExpression="FirstName" />

                <asp:BoundField DataField="MiddleName" HeaderText="MiddleName" SortExpression="MiddleName" />

                <asp:BoundField DataField="LastName" HeaderText="LastName" SortExpression="LastName" />

            </Columns>

            <EditRowStyle BackColor="#999999" />

            <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

            <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

            <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />

            <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />

            <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />

            <SortedAscendingCellStyle BackColor="#E9E7E2" />

            <SortedAscendingHeaderStyle BackColor="#506C8C" />

            <SortedDescendingCellStyle BackColor="#FFFDF8" />

            <SortedDescendingHeaderStyle BackColor="#6F8DAE" />

        </asp:GridView>

    </div>

    </form>

</body>

</html>

Voici ce que le profiler SQL nous indique comme activité :

Alors oui nous retrouvons notre select « SELECT Title, FirstName, MiddleName, LastName FROM Person.Contact », mais par contre comment fais ce petit malin pour le paging et le tri ? Tout simplement en récupérant tous les enregistrements et en faisant le traitement côté client.

LinqDataSource

Avant de faire la page, on doit créer notre fichier « *.dbml » et faire un mapping. Ensuite nous pouvons utiliser notre LinqDataSource.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="LinqDataSource.aspx.cs" Inherits="LinqDataSource" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

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

<head runat="server">

    <title>LINQDataSource</title>

</head>

<body>

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

    <asp:LinqDataSource ID="x_oLinqDataSource" runat="server" ContextTypeName="LinqAdventureWorksDataContext"

        EntityTypeName="" Select="new (Title, FirstName, MiddleName, LastName)"

        TableName="Contacts">

    </asp:LinqDataSource>

    <div>

        <asp:GridView ID="x_grdContacts" runat="server" AllowPaging="True" AllowSorting="True"

            AutoGenerateColumns="False" CellPadding="4" DataSourceID="x_oLinqDataSource"

            ForeColor="#333333" GridLines="None">

            <AlternatingRowStyle BackColor="White" ForeColor="#284775" />

            <Columns>

                <asp:BoundField DataField="Title" HeaderText="Title" ReadOnly="True" SortExpression="Title" />

                <asp:BoundField DataField="FirstName" HeaderText="FirstName" ReadOnly="True" SortExpression="FirstName" />

                <asp:BoundField DataField="MiddleName" HeaderText="MiddleName" ReadOnly="True" SortExpression="MiddleName" />

                <asp:BoundField DataField="LastName" HeaderText="LastName" ReadOnly="True" SortExpression="LastName" />

            </Columns>

            <EditRowStyle BackColor="#999999" />

            <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

            <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

            <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />

            <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />

            <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />

            <SortedAscendingCellStyle BackColor="#E9E7E2" />

            <SortedAscendingHeaderStyle BackColor="#506C8C" />

            <SortedDescendingCellStyle BackColor="#FFFDF8" />

            <SortedDescendingHeaderStyle BackColor="#6F8DAE" />

        </asp:GridView>

    </div>

    </form>

</body>

</html>

Nous retrouvons du basique, notre contexte, table et contenu du « SELECT ». Que trouvons-nous côté SQL ?

Ici nous retrouvons des lots de deux requêtes :

SELECT COUNT(*) AS [value]

FROM [Person].[Contact] AS [t0]

exec sp_executesql N'SELECT [t1].[Title], [t1].[FirstName], [t1].[MiddleName], [t1].[LastName]

FROM (

    SELECT ROW_NUMBER() OVER (ORDER BY [t0].[Title], [t0].[FirstName], [t0].[MiddleName], [t0].[LastName]) AS [ROW_NUMBER], [t0].[Title], [t0].[FirstName], [t0].[MiddleName], [t0].[LastName]

    FROM [Person].[Contact] AS [t0]

    ) AS [t1]

WHERE [t1].[ROW_NUMBER] BETWEEN @p0 + 1 AND @p0 + @p1

ORDER BY [t1].[ROW_NUMBER]',N'@p0 int,@p1 int',@p0=0,@p1=10

Le LinqDataSource demande à chaque affichage le nombre d’enregistrements, ensuite on rapatrie uniquement les enregistrements requis grâce à l’utilisation de ROW_NUMBER() apparu avec SQL Server 2005. Notons tout de même qu’un tri sur toutes les colonnes est systématiquement demandé.

EntityDataSource

Avant de faire la page, on doit créer notre fichier « *.edmx » et faire un mapping. Ensuite nous pouvons utiliser notre EntityDataSource.

Rien que du traditionnel ici, une petite astuce pour que le tri fonctionne, il faut bien définir  « AutoGenerateOrderByClause » à « true » ainsi que la section « OrderByParameters ».

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="EntityDataSource.aspx.cs"

    Inherits="EntityDataSource" %>

 

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

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

<head runat="server">

    <title>EntityDataSource</title>

</head>

<body>

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

    <asp:EntityDataSource ID="x_oEntityDataSource" runat="server"

        AutoGenerateOrderByClause="True" ConnectionString="name=EntityAdventureWorks"

        DefaultContainerName="EntityAdventureWorks" EnableFlattening="False" EntitySetName="Contact"

        EntityTypeFilter="Contact"

        Select="it.[Title], it.[FirstName], it.[MiddleName], it.[LastName]">

        <OrderByParameters>

            <asp:Parameter DefaultValue="Title" />

        </OrderByParameters>

    </asp:EntityDataSource>

    <div>

        <asp:GridView ID="x_grdContacts" runat="server" AllowPaging="True" AllowSorting="True"

            CellPadding="4" DataSourceID="x_oEntityDataSource" ForeColor="#333333" GridLines="None"

            AutoGenerateColumns="False">

            <AlternatingRowStyle BackColor="White" ForeColor="#284775" />

            <Columns>

                <asp:BoundField DataField="Title" HeaderText="Title" ReadOnly="True" SortExpression="Title" />

                <asp:BoundField DataField="FirstName" HeaderText="FirstName" ReadOnly="True" SortExpression="FirstName" />

                <asp:BoundField DataField="MiddleName" HeaderText="MiddleName" ReadOnly="True" SortExpression="MiddleName" />

                <asp:BoundField DataField="LastName" HeaderText="LastName" ReadOnly="True" SortExpression="LastName" />

            </Columns>

            <EditRowStyle BackColor="#999999" />

            <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

            <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />

            <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Center" />

            <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />

            <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />

            <SortedAscendingCellStyle BackColor="#E9E7E2" />

            <SortedAscendingHeaderStyle BackColor="#506C8C" />

            <SortedDescendingCellStyle BackColor="#FFFDF8" />

            <SortedDescendingHeaderStyle BackColor="#6F8DAE" />

        </asp:GridView>

    </div>

    </form>

</body>

</html>

Voici ce que le Profiler SQL nous apporte :

Nous retrouvons ici la même mécanique que notre LinqDataSource, c’est-à-dire, une requête pour connaître le nombre d’enregistrements et une autre pour rapatrier le nombre d’enregistrement de la page à afficher. Par contre, le tri est appliqué uniquement sur la colonne demandée et non toutes les colonnes comme la LinqDataSource. Voici les requêtes

SELECT

[GroupBy1].[A1] AS [C1]

FROM ( SELECT

      COUNT(1) AS [A1]

      FROM [Person].[Contact] AS [Extent1]

)  AS [GroupBy1]

SELECT TOP (10)

[Project1].[C1] AS [C1],

[Project1].[Title] AS [Title],

[Project1].[FirstName] AS [FirstName],

[Project1].[MiddleName] AS [MiddleName],

[Project1].[LastName] AS [LastName]

FROM ( SELECT [Project1].[Title] AS [Title], [Project1].[FirstName] AS [FirstName], [Project1].[MiddleName] AS [MiddleName], [Project1].[LastName] AS [LastName], [Project1].[C1] AS [C1], row_number() OVER (ORDER BY [Project1].[Title] ASC) AS [row_number]

      FROM ( SELECT

            [Extent1].[Title] AS [Title],

            [Extent1].[FirstName] AS [FirstName],

            [Extent1].[MiddleName] AS [MiddleName],

            [Extent1].[LastName] AS [LastName],

            1 AS [C1]

            FROM [Person].[Contact] AS [Extent1]

      )  AS [Project1]

)  AS [Project1]

WHERE [Project1].[row_number] > 0

ORDER BY [Project1].[Title] ASC

Bien que syntaxiquement très différente de celles de notre LinqDataSource, ces requêtes sont sémantiquement identiques. En effet, elles ont exactement le même plan d’exécution et temps d’exécution.

Conclusion

Tout d’abord, n’oublions pas le nombre de maquettes qui finissent en ligne trop rapidement. Dans ce sens, il faut oublier le SqlDataSource sauf à avoir une interdiction formelle à LinqDataSource ou EntityDataSource. Sinon, l’utilisation de LinqDataSource ou EntityDataSource peut se faire selon les préférences ou affinités de chacun.