Author Archives: Jiří Kanda

Výjimky „The I/O operation has been aborted…“ na IIS7

Nedávno se nám v IIS7 na serveru (Windows Server 2008 Standard x64 EN) začaly množit výjimky:

Exception information:
    Exception type: System.Runtime.InteropServices.COMException
    Exception message: The I/O operation has been aborted because of either a thread exit or
an application request. (Exception from HRESULT: 0x800703E3)

Nejdřív k výjimkám docházelo jen tak občas, ale vzestup počtu byl doslova raketový a naše aplikace se staly nepoužitelnými. Proč chyby vznikly nevíme, máme malé podezření na aktualizaci kb947562, ale situace se po jejím odinstalování nezlepšila.
Zdá se, že vyřešit popsaný problém pomohla změna nastavení Managed pipeline mode aplikačního poolu z „Integrated“ na „Classic“.

Nekonzistence vlastnosti Visible třídy System.Web.UI.Control

Vlastnost Visible třídy Control používáme k zobrazení a skrytí controlu ve stránce. Dokumentace říká něco mírně komplikovanějšího: Gets or sets a value that indicates whether a server control is rendered as UI on the page. Tato zdánlivá komplikovanost má zásadní význam. Getter property totiž vrací false, pokud je skrytý control nebo některý z jeho rodičů ve stromu controlů.
Z mého pohledu jde o dosti nezvyklé a vůči ostatním vlastnostem nekonzistentní chování. Neočekával bych, že po nastavení vlastnosti na true budu číst false. Doporučuji tedy s vlastností Visible pracovat náležitě opatrně.

Příklad

Mějme dva panely (PrvniPanel a DruhyPanel), přičemž chceme, aby byl vidět právě jeden z nich. Oba panely jsou obaleny vnějším panelem.

<asp:Panel ID="VnejsiPanel" Visible="false" runat="server">
        <asp:Panel ID="PrvniPanel" runat="server">
        ...
        </asp:Panel>
        <asp:Panel ID="DruhyPanel" runat="server">
        ...
        </asp:Panel>
</asp:Panel>
PrvniPanel.Visible = true; // nějaký výraz, který tentokrát vrací true
DruhyPanel.Visible = !PrvniPanel.Visible
VnejsiPanel.Visible = true;
  • Na řádku 1 nastavíme první panel k zobrazení.
  • Na řádku 2 přečteme property PrvniPanel.Visible. Ačkoliv jsme ji na řádku 1 nastavili na true, vrací false, neboť rodič VnejsiPanel ve stromu controlů je skrytý. I druhému panelu tedy nastavíme Visible na true.
  • Na řádku 3 nastavíme vnější panel k zobrazení.
  • Postupně jsme všem panelům nastavili Visible na true a bude tedy vidět vše, ačkoliv to tak na první pohled nevypadá.

Nekonzistence vlastnosti Visible třídy System.Web.UI.Control

 

Vlastnost Visible třídy Control používáme k zobrazení a skrytí controlu ve stránce. Dokumentace říká něco mírně komplikovanějšího: Gets or sets a value that indicates whether a server control is rendered as UI on the page. Tato zdánlivá komplikovanost má zásadní význam. Getter property totiž vrací false, pokud je skrytý control nebo některý z jeho rodičů ve stromu controlů.
Z mého pohledu jde o dosti nezvyklé a vůči ostatním vlastnostem nekonzistentní chování. Neočekával bych, že po nastavení vlastnosti na true budu číst false. Doporučuji tedy s vlastností Visible pracovat náležitě opatrně.

Příklad

Mějme dva panely (PrvniPanel a DruhyPanel), přičemž chceme, aby byl vidět právě jeden z nich. Oba panely jsou obaleny vnějším panelem.

<asp:Panel ID="VnejsiPanel" Visible="false" runat="server">
        <asp:Panel ID="PrvniPanel" runat="server">
        ...
        </asp:Panel>
        <asp:Panel ID="DruhyPanel" runat="server">
        ...
        </asp:Panel>
</asp:Panel>
PrvniPanel.Visible = true; // nějaký výraz, který tentokrát vrací true
DruhyPanel.Visible = !PrvniPanel.Visible
VnejsiPanel.Visible = true;
  • Na řádku 1 nastavíme první panel k zobrazení.
  • Na řádku 2 přečteme property PrvniPanel.Visible. Ačkoliv jsme ji na řádku 1 nastavili na true, vrací false, neboť rodič VnejsiPanel ve stromu controlů je skrytý. I druhému panelu tedy nastavíme Visible na true.
  • Na řádku 3 nastavíme vnější panel k zobrazení.
  • Postupně jsme všem panelům nastavili Visible na true a bude tedy vidět vše, ačkoliv to tak na první pohled nevypadá.
  • Registrace klientských skriptů: ClientScriptManager vs. ScriptManager

    ClientScriptManager

    Třída ClientScriptManager je součástí .NET Frameworku od jeho vzniku. Její instance je běžně přístupná přes Page.ClientScript. Třída slouží k registraci klientských skriptů, které mají být ve stránce renderovány, a k další práci s klienskými skripty.
    Tato třída neví nic o AJAXu a asynchronním postbacku, pokud zaregistrujeme do stránky nějaký klientský skript, bude vyrenderován pouze v případě prvního načtení stránky (GET) nebo v klasickém postbacku (POST). Pokud je skript registrován v asynchronním postbacku, do browseru se nedostane.

    ScriptManager

    Třída ScriptManager je součástí ASP.NET Ajax 1.0 rozšířující .NET Framework 2.0 nebo .NET Frameworku 3.5. Tato třída rovněž slouží k registaci klientských skriptů do stránky.
    Metody pro registraci klienských skriptů jsou statické a disponují rozhaním pro pohodlnější použití. Skripty registrované při prvním načítání stránky (GET) a v klasickém postbacku (POST) jsou stejně jako v předchozím případě renderovány do stránky, skripty registrované v asynchronním postbacku MOHOU být předány do browseru uživatele.
    Každá z registračních metod existuje ve dvou přetíženích, které se liší typem prvního parametru – Control vs. Page.
    Například:

    • RegisterClientScriptBlock(Page, Type, String, String, Boolean)
      Skripty registrované touto metodou jsou do browseru předány v každém asynchronním postbacku.
    • RegisterClientScriptBlock(Control, Type, String, String, Boolean)
      Skripty registrované touto metodou v asynchronním postbacku jsou renderovány do stránky jen tehdy, pokud je renderován předaný control. Pokud tedy při asnychronním postbacku není control v update panelu nebo je v update panelu, který není renderován, potom není renderován ani registrovaný skript.

    Další metody pro registraci klientských skriptů jsou:

    • RegisterClientScriptBlock – registruje blok kódu
    • RegisterClientScriptInclude – registruje externí soubor s klienských skriptem
    • RegisterClientScriptResource – registruje soubor s klienských skriptem z resources
    • RegisterOnSubmitStatement – registruje kód vykonaný před postbackem
    • RegisterStartupScript – registruje kód vykonaný během načtení stránky

    Vytvoření loginu pro doménovou skupinu

    V management nástrojích pro SQL Server 2005 nelze založit login pro doménovou skupinu.
    Login se mi podařilo založit pomocí příkazu

    create login [HAVIT\Development] from windows
    

    kde v našem případě je Development doménovou skupinou.
    Další pozorování je, že login doménové skupiny není možné pomocí management studia zakázat:

    • při nastavení Login na Disabled je zobrazena chyba,
    • nastavení Permissions to connect to database engine na Deny je ignorováno.

    Vytvoření loginu pro doménovou skupinu

    V management nástrojích pro SQL Server 2005 nelze založit login pro doménovou skupinu.
    Login se mi podařilo založit pomocí příkazu

    create login [HAVIT\Development] from windows
    

    kde v našem případě je Development doménovou skupinou.
    Další pozorování je, že login doménové skupiny není možné pomocí management studia zakázat:

    • při nastavení Login na Disabled je zobrazena chyba,
    • nastavení Permissions to connect to database engine na Deny je ignorováno.

    Použití C# 3.0 pro runtime .NET Framework 2.0

    Pokud vyvíjíte aplikaci ve Visual Studiu 2008 a aplikace je určena pro runtime .NET Framework 2.0, nic vám nebrání použít nové charakteristiky jazyka C# 3.0. Úžasné je, že pro to nemusíte udělat vůbec nic.
    Není možné pochopitelně použít ty rysy jazyka, které vyžadují knihovny instalované .NET Frameworkem 3.5. Můžete využít například eleganci lambda výrazů nebo zkráceného zápisu vlastností, zapomeňte ale na LINQ.

    Jak přepsat PageStatePersister

     

    Jak již bylo napsáno v jiném našem článku, vlastnost PageStatePersister slouží k získání objektu, který zajišťuje uložení ViewState. Článek detailně popisuje použití třídy SessionPageStatePersister, která zajišťuje uložení ViewState do Session.

    Chybná implementace

    Pokud chceme použít SessionPageStatePersister bez konfigurace pomocí adaptéru, musíme přepsat zmíněnou vlastnost, ideálně ve společném předkovi všech stránek v aplikaci. Nejjednodušší (avšak nesprávná implementace) může při každém dotazu na hodnotu vlastnosti vyrobit instanci persisteru a tu vrátit.

    protected override PageStatePersister PageStatePersister
    {
        get
        {
            // takto ne!
            new SessionPageStatePersister(this.Page);
        }
    }
    
    Symptomy

    Dosáhneme sice požadovaného efektu – ViewState se ukládá do Session, ovšem v dříve funkčních částech aplikace začneme pozorovat nečekané efekty:

    1. Mezi postbacky se nezachovávají některé hodnoty ukládané do control state (například DataKeys u GridView).
    2. V některých velmi specifických případech se neprovede nastavení hodnoty do vlastnosti, pokud tato vlastnost již měla hodnotu nastavenou v souboru ASPX a hodnota vlastnosti se vnitřně ukládala do ViewState (po provedení příkazMujControl.Vlastnost = novaHodnota nebude hodnota vlastnosti změněna).
    Správná implementace

    Ačkoliv se to nedočteme v dokumentaci, ale zjistíme po bližším zkoumání .NET reflektorem, musíme v jedné instanci třídy Page vracet stále stejnou instanci, protože na hodnotu vlastnosti se opakovaně dotazují metody třídy Page (jednou z nich je metoda RegisterRequiresControlState, což by vysvětlovalo symptomy v bodu 1). Po zapracování tohoto poznatku jsme se zmíněných problémů zbavili. Správně je tedy třeba vlastnost PageStatePersister přepsat takto:

    protected override PageStatePersister PageStatePersister
    {
        get
        {
            if (_pageStatePersister == null)
            {
                _pageStatePersister = new SessionPageStatePersister(this.Page);
            }
            return _pageStatePersister;
        }
    }
    private PageStatePersister _pageStatePersister;
    

    NullReferenceException v DefaultWsdlHelpGenerator.aspx při přístupu k webovým službám

    Vytváříme klasickou webovou službu publikovanou pomocí ASMX. Při pokusu o přístup k této webové službě přes internetový prohlížet obdržíme výjimku NullReferenceException – a to ještě žádnou metodu nevoláme, jen prohlížíme dostupné služby.
    Debugger se nám zastavuje v souboru DefaultWsdlHelpGenerator.aspx na řádku 1335:

    OperationBinding FindHttpBinding(string verb) {
            foreach (ServiceDescription description in serviceDescriptions) {
                foreach (Binding binding in description.Bindings) {
                    HttpBinding httpBinding = (HttpBinding)binding.Extensions.Find(typeof(HttpBinding));
    

    Vskutku pozoruhodné je pak řešení problému: Ve web.configu máme v sekci <pages> nastavení autoEventWireUp=“false“. Po odstranění tohoto nastavení přístup k dokumentaci webových služeb funguje.
    DefaultWsdlHelpGenerator.aspx je generátor dokumentace pro zobrazení v prohlížeči. Ten se evidentně kompiluje s použitím nastavení aplikace a v kódu spoléhá na zavolání metody Page_Load pomocí automatického navázání vybraných událostí (AutoEventWireUp). K dispozici máme i zdrojový kód tohoto souboru, standardně se nachází v C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG.
    (Runtime .NET Framework 2.0)

    Workaround

    Na vývojářské mašině se dá taky rovnou zeditovat soubor C:\Windows\Microsoft.NET\Framework\v2.0.50727\CONFIG\DefaultWsdlHelpGenerator.aspx a na první řádek přidat:

    <%@ Page AutoEventWireup="true" %>
    

    Firefox velmi pomalý při použití Windows Vista a ASP.NET Development serveru

    Načítání webových stránek je pod Firefoxem v kombinaci s Windows Vista a ASP.NET Development serverem velmi pomalé. První podezření padlo na ASP.NET AJAX pod Firefoxem, nicméně pravda se ukázala být jinde.

    Za všechno může automatické ladění TCP stacku ve Windows Vista. Na internetu jsem nalezl dvě řešení:

    Zákaz automatického ladění TCP stacku…

    … lze provést spuštěním „netsh interface tcp set global autotuninglevel=disabled“ z příkazové řádky.

    Toto řešení jsem nezkoušel.

    Zákaz IPv6 ve Firefoxu

    Dle návodu pomůže ve Firefoxu:

    1. přejít na stránku about:config,
    2. vyhledat položku network.dns.disableIPv6,
    3. nastavit hodnotu na true.

    Toto řešení bylo jednoduché a účinné.

    Jaká je Vaše zkušenost s kombinací Firefoxu, Windows Vista a ASP.NET Development serveru? Nebo jste na stejný problém narazili i při jiné kombinaci produktů?