Kamil Kliczbor @ asptip.net

5Dec/130

Castle Windsor – tryb życia komponentów w przykładzie

Castle Windsor

Wprowadzenie

Wpis nawiązuje do poprzedniego, teoretycznego wprowadzenia do trybów życia w Windsor Castle. W tym przykładzie spróbujemy posłużyć się semi-biznesowym przypadkiem. Powiedzmy, że mamy system do składania zamówień, który na podstawie kodu produktu oraz pewnej konfiguracji, będzie potrafił udzielić zniżki na wybrany produkt. Zepniemy te bardzo "wybujałe" wymagania i dostarczymy projekt w technologii ASP.NET MVC.

30Mar/110

Codingtv – wideoblog

Niniejszy wpis dedykuję twórcom strony, a raczej wideo-bloga codingtv.pl. Jest to całkiem innowacyjna i nowatorska forma promocji tworzenia oprogramowania w technologiach .NET-opodobnych.  Twórcami strony są dwaj programiści - Łukasz Gąsior, który posiada blog techniczny pod adresem http://gasior.net.pl oraz Andrzej Kowal. Obydwa osobniki pracują w jednej firmie - PGS Software, która udostępnia im salę do nagrywania kolejnych wideo-castów. W sumie jeszcze nie spotkałem do tej pory takiej formy nauczania na polskich stronach - wyjątek stanowi oczywiście kurs C#, który jest umieszczany regularnie na stronach virtualstudy.pl.

Koledzy w swoich wideo-castach opisują proces tworzenia bloga w C#. Technologie, których używają są "najświeższe". Wśród nich można wymienić m.in. ASP.NET MVC w wersji 3 czy Entity Framework. W kolejnych wydaniach wideocastów rzetelnie i skrupulatnie opisują dlaczego i jakie kroki podejmują, aby z pustego projektu utworzyć platformę do zarządzania blogiem. Zadowalające jest to, że nie leją wody, ale walą prosto z mostu i moim skromnym zdaniem umieszczają najważniejsze informacje. Esencja prosto w gały. Myślę, że może to być  całkiem ciekawe doświadczenie dla młodych programistów "szukających swojej drogi".

Kolejnym pocieszającym faktem jest to, że koledzy chcą prowadzić dialog. Tak jak jest to w muzyce, zgodnie z zasadą "call and response" potrafią słuchać swoich słuchaczy, a raczej można by rzecz "ogladaczy" i potrafią przy tym prowadzić konwersację. Dzięki temu powstaje swoisty dialog, dzięki któremu również siła internetowej społeczności ma wpływ na treści publikowane w następnych odcinkach. Nie wiem na ile naszym kolegom starczy sił, ale to co robią jest moim zdaniem dobre. Przyczyniają się do powstawania dobrych nawyków tworzenia oprogramowania w oparciu o najnowsze technologie, wzorce oraz metodologie. Swoją drogą, gdybym miał dostęp do takiej łatwo przyswajalnej wiedzy, to nie musiał bym w swoim developerskim życiu błądzić w jakichś bliżej nieznanych okolicach.

Polecam i gratuluje twórcom codingtv.pl - oby tak dalej!

Tagged as: , , No Comments
26Nov/102

Odświeżamy certifikat: upgrade certifikatu MCDP Web Developer z .NET 2.0 do .NET 4

Możliwe ścieżki migracji

Jak to mówią - Przyszła kryska na Matyska. Stary zaśniedziały i zardzewiały certyfikat MCPD Web Developer we Frameworku 2.0 wypadałoby uaktualnić do najnowszej wersji. Migracja nie jest wygodna, ponieważ możliwe ścieżki zakładają zdanie co najmniej dwóch egzaminów (link z pytaniem o migrację znajduje się tutaj).

Pierwsza ścieżka, która polega na zdaniu dwóch egzaminów wymaga podejścia do następujących sprawdzianów wiedzy:

  1. Exam 70-567: UPGRADE: Transition your MCPD Web Developer Skills to MCPD ASP.NET Developer 3.5
  2. Exam 70-523: Upgrade: Transition your MCPD .NET Framework 3.5 Web Developer Skills to MCPD .NET Framework 4 Web Developer

Z tym, że ostatni egzamin został udostępniony stosunkowo niedawno, tj. 22 października 2010.

Druga opcja polega po prostu na podjęciu standardowej ścieżki dla .NET Frameworka w wersji 4. Obejmuje on przystąpienie do serii następujących egzaminów:

  1. Exam 70-515: TS: Web Applications Development with Microsoft .NET Framework 4
  2. Exam 70-513: TS: Windows Communication Foundation Development with Microsoft .NET Framework 4
  3. Exam 70-516: TS: Accessing Data with Microsoft .NET Framework 4
  4. Exam 70-519: Pro: Designing and Developing Web Applications Using Microsoft .NET Framework 4

Jak widać różnica polega na ilości zdawanych egzaminów, co bezpośrednio przekłada się na koszta z tym związane oraz czas spędzony w centrum certyfikacyjnym. Ponieważ w tym momencie potrzebny mi jest MCPD z aplikacji webowych, będę dążyć do uzyskania upgradu korzystając z pierwszej ścieżki. Dodatkowo przemawiającym do mnie argumentem jest to, że nie ma wystarczającej ilości materiałów przygotowujących do egzaminów w postaci Self Paced Training Kitów w .NET Framework 4 na ten moment.

Książkowe materiały dydaktyczne

No i tutaj zaczynają się schody. Generalnie w przypadku migracji z .NET 2.0 na 3.5 oraz 3.5 do 4 nie istnieją Preparation Kity przygotowujące do egzaminu 70-567 i 70-523. Jedynym sensownym rozwiązaniem jest skorzystanie z materiałów przygotowanych bezpośrednio pod egzaminy MCTS-owe i MCPD-kowe z 3.5, czyli:

W przypadku MCPD-ka na 3.5 wydawnictwo Microsoft Press pokusiło się o wydanie książki przygotowującej do egzaminu, jednakże nie jest ona już w sprzedaży (? informacje z jakiegoś forum msdn) (na amazonie jest jeszcze dostępna). Być może z powodu złej opinii o książce została ona wycofana z druku.

Inna alternatywa polega na korzystaniu z książek z wydawnictw WROX-a, APRESS-a czy innych. Jednakże nie przygotowują one wprost do egzaminu. A wiadomo, że lepiej jest przeczytać książke pod egzamin niż zawierającą ogólną wiedzę.

Gdzie szukać materiałów w sieci?

Nie pozostaje nic innego jak przeszukać sieć pod kątem dostępnych informacji. Najwięcej materiałów znaleźć można oczywiście na MSDN-ie, jednakże przedstawiam poniżej kilka przydatnych linków, które raczej mogą bardziej pomóc niż zaszkodzić.

Nowe funkcjonalności w relacji 2.0 - 3.5:

Nowe funkcjonalności w relacji .NET 3.5 - 4.

Podsumowanie

Dużo, dużo jest do ogarnięcia. W szczególności, że w zakres MCPD-ka dla .NET 4 dochodzi WCF, ADO.NET i ASP.NET MVC. Dla programisty, który obraca się wokół tych tematów, zakres materiału nie będzie  trudny do zrozumienia. Trudności pojawią się raczej w zapamiętaniu i utrwaleniu tych informacji w ilości wystarczającej do podejścia i zdania egzaminu. W szczególności, że ASP.NET MVC to zcela nové.

A teraz już tylko: Do pracy rodacy!

20Nov/100

Krótka historia: jquery.validate + WebForms + zewnętrzy plik js

Jak do tego doszło ?

Post ten powstał z inicjatywy kolegi zadającego pytanie zadane na forum http://codeguru.pl/frmThread.aspx?tid=100619. Odpowiadając mu wydawało mi się, że rozwiązanie jest na tyle oczywiste, ze nie wymaga mojej "interwencji" w jego kod. Wszakże kod to rzecz święta :). Jednak późniejsze boje wykazały, że nie wszystko jest takie oczywiste jak się wydawać mogło na pierwszy rzut oka. Temat oczywiście dotyczy utworzenia standardowej stronki w WebForms-ach, która ma być walidowana przez plugin do jquery - jquery.validate.

Przypadek trywialny: skrypt inline na stronie

W necie przykładów do takiego podejścia można znaleźć na prawdę wiele. Po krótkiej chwili w mojej przeglądarce wylądowało ponad 10 różnych stronek, gdzie podawane przykłady aż biły prostotą. Poniżej umieszczam te linki jeżeli ktoś ma może ochotę sobie popatrzeć jakie istnieją rozwiązania do tego zagadnienia:

  • http://www.dotnetcurry.com/ShowArticle.aspx?ID=310&AspxAutoDetectCookieSupport=1
  • http://jquery.bassistance.de/validate/demo/milk/
  • http://www.webreference.com/programming/javascript/jquery/form_validation/
  • http://stackoverflow.com/questions/1232641/jquery-validate-js-and-asp-net-master-pages
  • http://codeclimber.net.nz/archive/2008/05/14/how-to-manage-asp.net-validation-from-javascript-with-jquery.aspx
  • http://www.theaspnetguy.com/theaspnetguy_root/post/2009/05/25/JQuery-Validate-plugin-to-validate-a-ASPNET-Form.aspx

Przypadek rozszerzony : skrypt w pliku zewnętrznym

No i sprawa zaczęła się komplikować, kiedy trzeba było w elegancki sposób przesunąć skrypt javascript'owy do pliku zewnętrznego. Po kilku chwilach walki, z pomocą następujących wskazówek rozwiązałem problem:

  • http://stackoverflow.com/questions/1232465/
  • http://stackoverflow.com/questions/619816/jquery-validation-plugin-in-asp-net-web-forms
  • http://www.west-wind.com/WebLog/posts/252178.aspx
  • http://docs.jquery.com/Plugins/Validation/rules#.22add.22rules

Główny problem tego zadania polegał w gruncie rzeczy na tym, żeby w jakiś sensowny sposób przekazać identyfikatory inputów do skryptu walidującego. O ile samo wydobycie nazwy kontrolki po stronie klienta np. TextBoxa nie było trudne - <%=tbUserName.ClientId %>, o tyle przypięcie tych idków do jquery.validate wymagało większej gimnastyki.

Rozwiązanie

Zewnętrzny plik JavaScript:

var RegisterForm = {
    _controlsNames: new Array(),
    _registerForm: '',

    set_controlsNames: function (value) {
        RegisterForm._controlsNames = value;
    },

    set_registerForm: function (value) {
        RegisterForm._registerForm = value;
    },

    init: function () {

        $("#" + RegisterForm._registerForm).validate();

        $('#' + RegisterForm._controlsNames.user).rules('add', {
            required: true,
            minlength: 2,
            messages: {
                required: "* Required Field *",
                minlength: "* Please enter at least 2 characters *"
            }
        });

        $('#' + RegisterForm._controlsNames.email).rules('add', {
            required: true,
            email: true,
            messages: {
                required: "* Required Field *"
            }
        });
    }
}

Plik formularza:

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="JQueryValidation._Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <h2>
        Formularz i walidacja z jquery.validate
    </h2>
    <asp:Label AssociatedControlID="RegisterUser" Text="Użytkownik:" runat="server"/>
    <asp:TextBox ID="RegisterUser" runat="server" /><br />
    <asp:Label AssociatedControlID="RegisterEmail" Text="Email:" runat="server"/><asp:TextBox ID="RegisterEmail"
        runat="server" />
    <br />
    <asp:Button ID="RegisterButton" runat="server" Text="Rejestruj" CausesValidation="False" />
    <script src="Scripts/jquery.validate.js" type="text/javascript"></script>
    <script src="Scripts/register.js" type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
        $(function () {
            var controlsToValidate = {
                user: "<%= RegisterUser.ClientID %>",
                email: "<%= RegisterEmail.ClientID %>"
            };

            RegisterForm.set_controlsNames(controlsToValidate);
            RegisterForm.set_registerForm("<%= this.Master.Page.Form.ClientID %>");
            RegisterForm.init();
        });
    //]]>
    </script>
</asp:Content>

Dla bardziej dociekliwych umieszczam również spakowany projekt.

11Oct/090

Nie działa postback event dla imagebutton-a :/

Przy okazji przenoszenia wersji aplikacji na inna maszynę, okazało się, że nie chodzą postbacki dla kliknięć w ImageButtona. Po przeszukaniu zasobów w necie udało mi się zlokazlizować problem, jako "dobrze znany błąd enginu asp.net".
Okazało się, że pod FF gdy nie brakuje obrazu, wtenczas nie ma ImageCLickEvents jako punkt kliknięcia. Rozwiązenie jest proste, zawsze zaaplikować obrazek....

Filed under: ASP.NET No Comments
23Jul/090

AjaxToolkit Multi-word AutoCompleteExtender

Tworząc dynamiczne strony, często z konieczności potrzebujemy użyć podpowiedzi dla użytkownika. Z pomocą przychodzi oczywiście standardowo AjaxControlToolkit z kontrolką AutoCompleteExtender. Jest to kontrolka, która pozwala na wykorzystanie w wielu scenariuszach. Najprostszym z nich jest wyświetlanie podpowiedzi do wpisanego słowa w postaci listy. A co jeżeli chcielibyśmy, aby nasz "podpowiadator" pomagał przy wpisywaniu każdego słowa osobno ? Otóż można skonfigurować ten komponent tak, żeby nam to ułatwił. Parametry kontrolki:

<ajaxToolkit:AutoCompleteExtender ID="aceTags" runat="server" 

             TargetControlID="tbTags"

             ServiceMethod="GetCompletionList" 

             MinimumPrefixLength="2"

             ServicePath="~/WS/Service.asmx"

             CompletionInterval="400"

             DelimiterCharacters="," 

             ShowOnlyCurrentWordInCompletionListItem="true" />

Krótkie objaśnienie:
ServiceMethod to nazwa metody w webserwisie, do którego się odwołujemy, a
ServicePath to ścieżka do tego webserwisu.
DelimiterCharacters określa jaki znak będzie oddzielał poszczególne elementy,
i wreszcie chyba najważniejsze to to, że ustawienie paramteru ShowOnlyCurrentWordInCompletionListItem na true powoduje, że podpowiadany jest tylko bieżący element z listy.

No i śmiga :)

A teraz kilka błędów:
1. Tworzony web service musi mieć atrybut [ScriptService].
2. Metoda sweb servicu musi być public string[] MojaMetoda (string prefixText, int count), (opcjonalnie można dodać jeszcze jeden parametr - można doczytać). Trzeba zwrócić uwagę, że metoda nie jest statyczna i dodatkowo nie można modyfikować nazwy jej parametrów wejściowych. Sysgnatura metody może być dowolna. Metodę taką trzeba dodatkowo zaopatrzyć w atrybut [ScriptMethod].
3. Jeżeli coś nie działa, sprawdź czy możesz się dobić do webservisu. Ścieżka wygląda na taką modłe http://localhost/WS/Service.asmx, gdzie oczywiście katalog i nazwa serwisu zależą od własnej implementacji. Jeżeli WS jest widoczny, to powinieneś móc sobie wywołać twoją metodę z przeglądarki.
4. Sprawdź czy dobrze się odwołujesz do odpowiedniego WS w kodzie kontrolki.

26Jun/090

Cache na przykładzie .NET Pet Shop 4

Ciekawe jest, że jest to przykład dobrego stylu programowania. Między innymi zastosowano tam użycia cache w oparciu o Hashtable. Przeprowadzone testy wykazują, że użycie cache nie do końca przynosi spodziewane rezultaty:

using System;

using System.Data;

using System.Data.SqlClient;

 

namespace ConsoleApplication1

{

   class Program

   {

      static void Main(string[] args) {

 

         int[] lengths = PrepareParamsArray();

 

         DateTime start = DateTime.Now;

         for (var t1 = 0; t1 < lengths.Length; t1++) {

            GetWithCache(lengths[t1]);

         }

         DateTime stop = DateTime.Now;

         TimeSpan test1 = stop - start;

         Console.WriteLine("Test with cache time: {0} ms", test1.TotalMilliseconds);

 

         start = DateTime.Now;

         for (var t2 = 0; t2 < lengths.Length; t2++) {

            GetWithoutCache(lengths[t2]);

         }

         stop = DateTime.Now;

         TimeSpan test2 = stop - start;

         Console.WriteLine("Test without cache time: {0} ms", test2.TotalMilliseconds);

 

         start = DateTime.Now;

         for (var t3 = 0; t3 < lengths.Length; t3++) {

            GetWithCacheLonger(lengths[t3]);

         }

         stop = DateTime.Now;

         TimeSpan test3 = stop - start;

         Console.WriteLine("Test with cache long time: {0} ms", test3.TotalMilliseconds);

 

         start = DateTime.Now;

         for (var t2 = 0; t2 < lengths.Length; t2++) {

            GetWithoutCacheLonger(lengths[t2]);

         }

         stop = DateTime.Now;

         TimeSpan test4 = stop - start;

         Console.WriteLine("Test without cache long time: {0} ms", test4.TotalMilliseconds);

 

         Console.ReadKey();

      }

 

      private static SqlParameter[] GetWithCache(int i) {

         SqlParameter[] parms = TestSqlParams.GetParameters("TEST2" + i);

 

         if (parms == null) {

            parms = Get2Params(i);

            TestSqlParams.StoreParameters("TEST2" + i, parms);

         }

 

         return parms;

      }

 

      private static SqlParameter[] GetWithoutCache(int i) {

         return Get2Params(i);

      }

      

      private static SqlParameter[] GetWithCacheLonger(int i) {

         SqlParameter[] parms = TestSqlParams.GetParameters("TEST10" + i);

 

         if (parms == null) {

            parms = Get10Params(i);

            TestSqlParams.StoreParameters("TEST10" + i, parms);

         }

 

         return parms;

      }

 

      private static SqlParameter[] GetWithoutCacheLonger(int i) {

         return Get10Params(i);

      }

 

      private static SqlParameter[] Get2Params(int i) {

         var parms = new[] {

                    new SqlParameter("@Quantity" + i, SqlDbType.Int),

                    new SqlParameter("@ItemId" + i, SqlDbType.VarChar, 10)};

 

         return parms;

      }

 

      private static SqlParameter[] Get10Params(int i) {

         var parms = new[] {

                    new SqlParameter("@Quantity" + i, SqlDbType.Int),

                    new SqlParameter("@ItemId" + i, SqlDbType.VarChar, 10),

               new SqlParameter("@Desc" + i, SqlDbType.Char, 120),

               new SqlParameter("@Amount" + i, SqlDbType.Int),

               new SqlParameter("@Price" + i, SqlDbType.Money),

               new SqlParameter("@ShipNo" + i, SqlDbType.VarChar, 40),

               new SqlParameter("@ShipPlace" + i, SqlDbType.NChar, 30),

               new SqlParameter("@Available" + i, SqlDbType.Bit),

               new SqlParameter("@CanSellPlace" + i, SqlDbType.NText, 1024),

               new SqlParameter("@Guid" + i, SqlDbType.UniqueIdentifier)

         };

 

         return parms;

      }

 

      public static int[] PrepareParamsArray() {

         const int arrLength = 1000000;

         var parmsLen = new int[arrLength];

 

         for (var i = 0; i < arrLength; i++) {

            parmsLen[i] = i % 45;

         }

         return parmsLen;

      }

 

   }

}

Oraz:

using System;

using System.Collections;

using System.Data.SqlClient;

 

namespace ConsoleApplication1

{

   class TestSqlParams

   {

      private static Hashtable cache = Hashtable.Synchronized(new Hashtable());

 

      public static void StoreParameters(string key, params SqlParameter[] parameters)

      {

         cache[key] = parameters;

      }

      

      public static SqlParameter[] GetParameters(string key)

      {

         var cached = (SqlParameter[]) cache[key];

         if (cached == null) return null;

 

         var cloned = new SqlParameter[cached.Length];

 

         for(int index = 0, cacheLen = cached.Length; index < cacheLen; index++)

         {

            cloned[index] = ((SqlParameter) ((ICloneable) cached[index]).Clone());

         }

 

         return cloned;

      }

   }

}

Wyniki są zadziwniające:
Test with cache time: 3432 ms
Test without cache time: 1070 ms
Test with cache long time: 16982 ms
Test without cache long time: 6349 ms

Filed under: Cache, Wydajność No Comments
31May/090

Dynamiczne wczytywanie Assembly i błąd "Could not load file or assembly"

Scenariusz tym razem wygląda następująco:
w solucji projekty: IDAL - interfejsy dla DAL, DaoFactory - wzorzec fabryki abstrakcyjnej dla całego DAL oraz Model - klasy modelowe, a także Tests - projekt testów jednostkowych.
Z poziomu projektu testów zostały dodane referencje do wszystkich niezbędnych projektów. Dllki znajdują się w katalogu bin/debug. Test:

[TestMethod]

public void CanSaveTag() {

   ITagDao tagDao = ChPortalFactory.CreateTagDao();

   Tag tag = new Tag { Name = "Testowy"};

   bool result = tagDao.Add(tag, 2);

   Assert.IsTrue(result);

}

wyrzuca wyjątek System.IO.FileNotFoundException: Could not load file or assembly (ChPortal.Model).

Klasa fabryka abstrakcyjnej wczytuje dynamicznie Assembly:

public static ITagDao CreateTagDao() {

   string className = DBConnectionString.WebDAL + ".TagDao";

   return (ITagDao)Assembly.Load(DBConnectionString.WebDAL).CreateInstance(className);

}

Pierwsze poszukiwania zacząłem od tego, czy na pewno dllki znajdują się w katalogu testów, potem zacząłem debugować test. Okazało się, że wszystko gra do momentu tworzenia instancji klasy TagDao. No ale co to miało wspólnego w klasą modelową? Otóż, jeżeli klasa TagDao odwołuje się do innego assembly, to również to assembly musi zostać dynamicznie załadowane.
Najpierw zacząłem szukać w necie i znalazłem dosyć duży wpis na blogu, który znajduje się pod tym adresem.
Ale co bardziej ciekawe, to właśnie narzędzie Assembly Binding Log Viewer (Fuslogvw.exe) umożliwiło mi znalezienie błędu.

Ech.. okazało się, że niepotrzebnie dodałem [assembly: AssemblyCulture("pl-PL")], więc Activator poszukiwał tego assembly w katalogu bin/debug/pl-PL.

23May/090

Bindowanie kontrolki DropDownList

Ostatnio, w sumie bezmyślnie, bindowałem listę przy użyciu obiektów ListItem w pętli. Jednak po krótkim zastanowieniu pomyślałem sobie, że jednak mając jakąś kolekcję generyczną, można by elegancko użyć właściwości: DataSource, DataValueField, oraz DataValueText.
Postanowiłem zrobić mały teścik i oto jego rezultaty:

Kod c#:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web.UI.WebControls; 

namespace TestBinding
{
   public partial class _Default : System.Web.UI.Page

   {

      public static List<DTO> _list = new List<DTO>();

      public class DTO

      {

         public string CustomerId { get; set; }
         public string ShipName { get; set; }

      }

 

      private readonly DataTable _dataTable = new DataTable();

 

      protected override void OnPreInit(EventArgs e) {

         base.OnPreInit(e);

 

         if (!IsPostBack) {

 

            using (

               SqlConnection connection =

                  new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ToString())) {

               SqlDataAdapter adapter = new SqlDataAdapter("SELECT [CustomerID], [ShipName] FROM [Orders]", connection);

               connection.Open();

               adapter.Fill(_dataTable);

               connection.Close();

            }

 

            foreach (DataRow row in _dataTable.Rows) {

               _list.Add(new DTO { CustomerId = row.ItemArray[0].ToString(), ShipName = row.ItemArray[1].ToString() });

            }

         }

      }

 

      protected void Page_Load(object sender, EventArgs e) {

         //test 1

         DateTime start;

         DateTime stop;

 

         start = DateTime.Now;

         DropDownList1.DataValueField = "CustomerId";

         DropDownList1.DataTextField = "ShipName";

         DropDownList1.DataSource = _list;

         DropDownList1.DataBind();

         stop = DateTime.Now;

         Label1.Text = (stop - start).Milliseconds.ToString();

 

         start = DateTime.Now;

         foreach (DTO dto in _list) {

            DropDownList2.Items.Add(new ListItem(dto.ShipName, dto.CustomerId));

         }

         stop = DateTime.Now;

         Label2.Text = (stop - start).Milliseconds.ToString();

 

         //test 2 - sql

 

         start = DateTime.Now;

         DropDownList3.DataValueField = "CustomerId";

         DropDownList3.DataTextField = "ShipName";

         DropDownList3.DataSource = _dataTable;

         DropDownList3.DataBind();

         stop = DateTime.Now;

         Label3.Text = (stop - start).Milliseconds.ToString();

 

         start = DateTime.Now;

         foreach (DataRow row in _dataTable.Rows) {

            DropDownList4.Items.Add(new ListItem(row.ItemArray[1].ToString(), row.ItemArray[0].ToString()));

         }

         stop = DateTime.Now;

         Label4.Text = (stop - start).Milliseconds.ToString();

      }
   }
}

Kod asp:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestBinding._Default" %>

 

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

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

<head runat="server">

   <title>Untitled Page</title>

</head>

<body>

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

   <div>

      Lista generyczna

      <br />

      DataBind() <asp:DropDownList ID="DropDownList1" runat="server">

      </asp:DropDownList>

      <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

      <br />

      <br />

      ListItem() <asp:DropDownList ID="DropDownList2" runat="server">

      </asp:DropDownList>

      <asp:Label ID="Label2" runat="server" Text="Label"></asp:Label>

      <br />

      <br />

      DataTable

      <br />

      DataBind() <asp:DropDownList ID="DropDownList3" runat="server">

      </asp:DropDownList>

      <asp:Label ID="Label3" runat="server" Text="Label"></asp:Label>

      <br />

      <br />

      ListItem() <asp:DropDownList ID="DropDownList4" runat="server">

      </asp:DropDownList>

      <asp:Label ID="Label4" runat="server" Text="Label"></asp:Label>

      <br />

   </div>

   </form>

</body>

</html>

Dane do bindowania wykorzystałem ze standardowej bazy Northwind, tabela Orders liczyła 830 rekordów.

Jak widać mimo "eleganckiego" podejścia do sprawy, rozwiązanie z DataBind() na liście rozwijanej wypada gorzej pod względem wydajnościowym niż w przypadku populowania listy z użyciem obiektu ListItem.