Author Archives: Robert Haken

avatar Neznámé

About Robert Haken

Software Architect, Founder at HAVIT, Microsoft MVP - ASP.NET/IIS

Vyhledávání nezávislé na diakritice pomocí COLLATE

Pomocí explicitního nastavení collation lze jednoduše udělat vyhledávání nezávislé na diakritice a velkých/malých písmenech:

 SELECT 'Ano!' WHERE ('ABCacžžzř' COLLATE Latin1_General_CI_AI LIKE 'abcáčžzžr' COLLATE Latin1_General_CI_AI) 

Celý tento způsob má několik problematických otazníků, které jsem zatím neprobádal:

  • není mi úplně jasné, jaký je vztah mezi collation a indexy, v mém konkrétním případě nedošlo ke změně execution planu a operace zůstala „Clustered Index Scan“.
  • není mi úplně jasné, proč některé collation fungují, a některé ne – např. „Czech_CI_AI“ mi očekávané výsledky nedávalo.

Update: Czech_CI_AI

Po letech jsem se dopátral, jak je to s Czech_CI_AI. Věc se má tak, že v češtině jsou C-Č, D-Ď, R-Ř, … různá písmena (na rozdíl od A-Á, E-Ě, …) a collation Czech_CI_AI to důsledně rozlišuje (na rozdíl od Latin1_General_CI_AI).

Vlastní primitivní Repeater – templated data-bound control

Primitivní Repeater

Začněme velmi jednoduchým příkladem, primitivní napodobeninou Repeateru, na níž si ukážeme základy vytváření složených datově-vázaných controlů (composite data-bound controls) a použití šablon v controlech (templates).

Dejme tomu chceme vytvořit control, který bude podle šablon vypisovat seznam zákazníků. Oproti standardnímu Repeateru z ASP.NET však bude pracovat se strong-typed prvky a přidáme si jednoduchou podporu pro načítání šablon ze souborů. Spokojíme se prozatím bez podpory ViewState (data bude tedy nutno bindovat v každém roundtripu) i bez eventů (IremCreated, ItemDataBound, Command, …).

<havit:MyRepeater ID="cosi" runat="server">
      <ItemTemplate>
         Jméno: <%# Container.Zakaznik.Jmeno %><br/>
         Datum narození: <%# Container.Zakaznik.DatumNarozeni.ToShortDateString() %></br>
      </ItemTemplate>
</havit:MyRepeater>

Základy práce se šablonami (templates)

Princip šablon je velmi jednoduchý. Když si uvědomíme, že controly tvoří hiearchii (kořenem je Page a podřízené controly jsou vždy v kolekci Controls), pak použití šablony není nic jiného než připojení nové větve controlů do této hiearchie.

V controlu se šablona samotná vytvoří pomocí property typu ITemplate.

[PersistenceMode(PersistenceMode.InnerPropety)]
[TemplateContainer(typeof(MyRepeaterItem))]
public ITemplate ItemTemplate
{
      get { return _itemTemplate; }
      set { _itemTemplate = value; }
}

Atribut PersistenceMode nastavený na InnerPropety nám říká, že ItemTemplate bude ve zdrojovém kódu controlu zapsán jako vnořený tag. Atribut TemplateContainerzase parseru říká, jakého typu je objekt Container, který budeme používat uvnitř template.

Pro začátek je to vše, co potřebujeme pro definici šablony, parser se sám postará o naplnění property ItemTemplate, pokud ji v .aspx stránce použijeme.

Rozhraní ITemplate definuje jedinou metodu InstantiateIn(Control container), která nedělá nic jednoduššího, než že připojí control-hiearchy tvořené šablonou jako větev do containeru (toho containeru, jehož typ jsme udali v atributu TemplateContainer).

Nutno zdůraznit, že container, do kterého template připojujeme, musí implementovat rozhraní INamingContainer, aby nám korektně fungoval objekt Container v kódu šablony.

Základy vytváření data-bound controls

Pro vytváření data-bound control je v ASP.NET 2.0 připraven pattern v podobě abstraktní třídy CompositeDataBoundControl. Composite proto, že nové controly vytváříme skládáním jiných controlů do hiearchie, nikoliv renderováním HTML kódu controlů úplně nových.

Základní implementace se v podstatě celá odehrává v metodě CreateChildControls(IEnumerable dataSource, bool dataBinding), přičemž základní pattern chování by měl být zhruba následující

override int CreateChildControls(IEnumerable dataSource, bool dataBiding)
{
      if (dataBinding)
      {
            // vytahej z dataSource data a ulož si je ve své podobě, obvykle do this.Items
            // this.Items bychom si měli ukládat do ViewState, drží nám data mezi postbacky
      }



      // vytvoř controly pro každou položku this.Items a přidej ji do this.Controls
      // foreach (MyItem item in Items) { ... Controls.Add(..); }
      // return počet vytvořených items
}

Pokud nemáme ViewState, tak lze samozřejmě obě fáze sloučit a rovnou tvořit control-hiearchy z dat v dataSource.

MyRepeater – kombinace CompositeDataBoundControl a ITemplate

No a není nic jednoduššího, než obě možnosti zkombinovat a vytvořit tak control, který bude data-bound a pro reprezentaci dat bude používat šablony:

public class MyRepeater : CompositeDataBoundControl
{
      [PersistenceMode(PersistenceMode.InnerPropety)]
      [TemplateContainer(typeof(MyRepeaterItem))]
      public ITemplate ItemTemplate
      {
            get { return _itemTemplate; }
            set { _itemTemplate = value; }
      }
      private ITemplate _itemTemplate;

      /// <summary>
      /// Soubor .ascx, který představuje případnou externí ItemTemplate.
      /// </summary>
      public string ItemTemplateFile
      {
            get { return _itemTemplateFile; }
            set { _itemTemplateFile = value; }
      }
      private string _itemTemplateFile;
      public Collection<MyRepeaterItem> Items
      {
            get
            {
                  if (_items == null)
                  {
                        _items = new Collection<MyRepeaterItem>();
                  }
                  return _items;
            }
      }
      private Collection<MyRepeaterItem> _items;
      protected override CreateChildControls(IEnumerable dataSource, bool dataBinding)
      {
            // vytaháme data z dataSource
            if (dataBinding)
            {
                  if (dataSource != null)
                  {
                        foreach (object o in dataSource)
                        {
                              if (!(o is Zakaznik))
                              {
                                    throw new ApplicationException("DataSource musí obsahovat prvky typu Zakaznik.");
                              }
                              MyRepeaterItem item = new MyRepeaterItem(o as Zakaznik);
                              Items.Add(item);
                        }
                  }
            }
            // vytvoříme controly
            foreach (MyRepeaterItem in Items)
            {
                  if (!String.IsNullOrEmpty(ItemTemplateFile))
                  {
                        _itemTemplate = Page.LoadTemplate(ItemTemplateFile);
                  }
                  _itemTemplate.InstantiateIn(item);
                  item.DataBind();
                  Controls.Add(item);
            }
            return Items.Count;
      }
}

Kód ukazuje i druhou možnost získávání šablon – jejich načítání z .ASCX souboru pomocí Page.LoadTemplate(). Šablona totiž může úplně stejně, jako ve vlastní .aspx stránce, být uložena v samostatném .ASCX souboru (jen pro Container je tam potřeba použít explicitní přetypování, protože tam parser neví o atributu TemplateContainer).

Pro úplnost ještě MyRepeaterItem:

public class MyRepeaterItem : Control, INamingContainer
{
      public Zakaznik Zakaznik
      {
            get { return _zakaznik; }
            set { _zakaznik = value; }
      }
      public MyRepeaterItem(Zakaznik zakaznik)
      {
            this._zakaznik = zakaznik;
      }
}
Odkazy na související články

Mozilla: nefunguje window.navigate(‚url‘)

Mozilla navigate(‚url‘) nepodporuje.

Místo toho je potřeba použít standardní

window.location.href = 'url';

…což funguje i v IE.

BoundField částečně ignoruje DataFormatString

Problém je v tom, že na BoundField je před formátováním aplikován HtmlEncode. Je to sicereportovaný a uznaný bug (špatně navrženo), nicméně z důvodu zpětné kompatibility budoucích verzí .NET Frameworku to už zřejmě zůstane v této podobě.

BoundField v GridView tedy ve výchozím nastavení nesprávně vyhodnotí DataFormatString u dat, čísel, atp. – například z hodnoty 0,123456 a formátu „aa {0:n1}“ udělá „aa 0,123456“.
Pomůže nastavit HtmlEncode = „false“, tedy vypnout onen problematický HtmlEncode.

<asp:BoundField
   DataField="MyField"
   DataFormatString="{0:n2}"
   HtmlEncode="false"
/>

NUMLOCK při startu Windows / po přihlášení

Spusťte Registry editor a najděte klíč HKEY_CURRENT_USER\Control Panel\Keyboard a nastavte hodnotu InitialKeyboardIndicators na 2. Tímto zapnete NUMLOCK při přihlášení.

[HKEY_CURRENT_USER\Control Panel\Keyboard]
"InitialKeyboardIndicators"=2

Pokud chcete zapnout NUMLOCK již při startu Windows, nastavte v klíči HKEY_USER\.DEFAULT\Control Panel\Keyboard hodnotu InitialKeyboardIndicators na 2.

[HKEY_USER\.DEFAULT\Control Panel\Keyboard]
"InitialKeyboardIndicators"=2

Jinou možností bez použití Registry editoru je prý přihlásit se jako administrátor nebo jako uživatel s administrátorskými právy a zapnout NumLock. Poté stačí restartovat Windows a při dalším startu se bude zapínat automaticky. Mně to ale většinou nepomůže, zabere až změna v registry…

ASP.NET WebForm Page Lifecycle Diagram (Léon Andianarivony)

Viz též můj podrobný diagram včetně request fází.

image

TRUNCATE TABLE vs. DELETE

Příkaz TRUNCATE TABLE vyprázdní tabulku a má tak efekt podobný příkazu DELETE (bez omezujících podmínek). Ve skutečnosti je však jejich funkce dosti odlišná.

TRUNCATE TABLE je ale rychlejší, používá méně systémových prostředků a méně prostředků transakčního logu, než DELETE (neloguje mazání každé jednotlivé řádky).

TRUNCATE TABLE navíc vyresetuje identity counter na počáteční hodnotu!!!

TRUNCATE TABLE neaktivuje TRIGGER !!!

DELETE vymaže řádky po jednom a do transaction-logu se zaznamená každé jednotlivé vymazání samostatně. Naproti tomu TRUNCATE TABLE vymaže data dealokací (uvolněním) datových stránek (data pages) použitých pro tabulku a do transaction-logu jsou zaznamenány jen tyto dealokace.

Exchange neodesílá, snaží se doručovat na divné IP adresy

Symptom

Pozoruhodné chování Exchange 2003 serveru. Neodchází maily do věšiny domén, zbytek zůstává ve frontě. Po bližším ohledání je v eventech vidět, že Exchange na některé pokusy vyhodí chybu, že mu protější strana neodpovídá. Z některých eventů je poznat, že komunikuje s podivnými IP adresami protější domény, které neodpovídají MX záznamům, ale jiným adresám v cílové doméně (nejpíš hlavní adresy domény, blíže jsem neprozkoumal).

Do domén, kde se adresa SMTP serveru náhodou shoduje s takto určenou IP adresou Exchange serveru, tak tam jsou maily doručeny v pohodě.

Příčina

Toto podivuhodné chování se projevilo na stroji Windows 2003 Server R2, který neměl na síťovém rozhraní nastaven DNS server. DNS služba však na něm běžela (to však nemusí být podstatné).

Každopádně stačilo nastavit DNS sám na sebe a vše se hned rozjelo. Čím resolvoval doposud, těžko říct. Očekával bych, že nebude resolvovat vůbec, ale nějak resolvoval…

Uložení výsledku stored procedury pomocí SELECT INTO

Když neznáme přesnou strukturu výsledku stored procedury, nemůžeme použít CREATE TABLE a INSERT … EXEC. Dá se to obejít pomocí OPENQUERY:

exec sp_serveroption [SERVER_NAME] , 'data access', 'true'

SELECT * INTO #W FROM OPENQUERY([SERVER_NAME], 'exec sp_who')
SELECT * FROM #W

SQL: Doplňování (padding) čísel nulami zleva na pevný počet míst (15 -> 00015)

Možná to jde lépe, ale co třeba takto:

= REPLACE(STR(15, 5, 0), ' ', '0')

Funkce STR(num, length, decimals) převede číslo na řetězec určené délky (doplněný zleva mezerami) a s daným počtem desetinných míst.

Nebo takto?

= RIGHT('00000' + cislo, 5)

Většinou si formátování řešíme až v prezentační vrstvě, ale někdy se to může hodit…