Windows 2000 Server: Event ID 1202, SceCli, 0xd : The data is invalid.

Tento warning se nám může množit v Application event logu, po aplikaci šablony zabezpečení Basicdc.inf. Potíž je v tom, že se tato šablona odkazuje na proměnné %SYSVOL%, %DSDIT% a %DSLOG“, které však neexistují (existují jen během Dcpromo procesu).

Stačí tedy tyto environment variables vytvořit. Výchozí složky jsou následující

%SYSVOL% = C:\WINNT\SYSVOL
%DSDIT% = C:\WINNT\NTDS
%DSLOG% = C:\WINNT\NTDS

…a warning je pryč.

Nested Repeaters – vnořování repeaterů

Vnořit Repeatery se může zdát potíž, dokud poprvé neuvidíte, jak je to jednoduché. Celý fígl totiž spočívá v data-bindingu vnitřních repeaterů v obsluze události ItemDataBound vnějšího Repeateru.

V příkladu vnější Repeater iteruje přes všechny obory činnosti (kategorie, skupiny) a vnitřní Repeater zobrazuje položky (zde „zápisy do katalogu“) příslušející danému oboru činnosti (kategorii, skupině).

MyPage.aspx

<asp:Repeater ID="OboryCinnostiRepeater" runat="server">
  <ItemTemplate>
     
   <%# ((OborCinnosti)Container.DataItem).Nazev %>
   
   <asp:Repeater ID="ZapisyRepeater" runat="server">
    <ItemTemplate>
      <%# ((ZapisDoKatalogu)Container.DataItem).Jmeno %>
     </ItemTemplate>
   </asp:Repeater>
  
  </ItemTemplate>
 </asp:Repeater>

MyPage.aspx.cs

private void OboryCinnostiRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
   RepeaterItem item = e.Item;
  
   // zajímají nás jen datové řádky, ne hlavička ani patička
   if ((item.ItemType == ListItemType.Item) || (item.ItemType == ListItemType.AlternatingItem))
   {
    // najdeme si vnitřní Repeater
    Repeater zapisyRepeater = (Repeater)item.FindControl("ZapisyRepeater");
  
    // a nabidnujeme mu data příslušející položce (oboru)
    OborCinnosti obor = (OborCinnosti)item.DataItem;
    ZapisDoKataloguCollection = obor.GetZapisy();
    
    zapisyRepeater.DataSource = zapisyOboru;
    zapisyRepeater.DataBind();
   }
}

File upload – HttpException (0x80004005): Request timed out.

Při uploadu velkých souborů (relativně dle rychlosti spojení) se můžeme setkat s chybou

HttpException (0x80004005): Request timed out.

Jde v podstatě o to, že IIS tlačí do ASP.NET data průběžně dlouhou dobu a request na straně ASP.NET vytimeoutuje. Stačí však nastavit parametr executionTimeout ve web.configu na dostatečnou hodnotu a je po problému:

<httpRuntime
     maxRequestLength="10240"
     executionTimeout="36000"
/>

…maxRequestLength je volba, na kterou se obvykle nezapomíná a která mění základní limit 4 MB (4096 KB) pro maximální velikost requestu (zadává se v KB, executionTimeout je v sekundách).

Download

Podle informací uživatele P.L. z newsgroupy microsoft.public.cs.developer vyřešil atributexecutionTimeout obdobný problém i s padáním dlouhotrvajících downloadů.

onBeforeUnload – Potvrzovací dialog před odchodem ze stránky

V browseru, na stránkách, kde dochází k editaci záznamů, či jiné aktivitě, kterou je potřeba zakončit uložením či volbou nějakého tlačítka, se nám může hodit využít události onBeforeUnload k zobrazení potvrzovacího dialogu s dotazem, zde si uživatel opravdu přeje stránku opustit.

<html>
<head>
   <script type="text/jscript">
      // inicializace  
      g_blnCheckUnload = true;     
      function RunOnBeforeUnload()
      {
         if (g_blnCheckUnload)
         {
            window.event.returnValue = 'Text, který bude přidán do confirmačního dialogu';
         }
      }
      function bypassCheck()
      { 
         g_blnCheckUnload  = false; 
      }
   </script>
</head>
<body onBeforeUnload="RunOnBeforeUnload();">
   <a href="http://www.havit.cz">dotaz zobrazen</a>
   <a href="http://www.havit.eu" onClick="bypassCheck">dotaz nezobrazen</a>
</body>
</html>

Událost onBeforeUnload se volá nejenom na odkazech a tlačítkách, ale i při zavírání okna prohlížeče a prakticky veškerých událostech, kde by mělo dojít k opuštění stránky.

Funguje to minimálně v Internet Exploreru a FireFoxu.

Modifikace s hlídáním změn

Nakonec se mi podařilo rozchodit i rozumnou podobu výše uvedeného, kdy je potvrzovací dotaz zobrazen jen při změně formulářových dat (a je tedy potřeba změny uložit):

<html>
<head>
   <script type="text/jscript">
      // inicializace  
      g_blnCheckUnload = false;     
      function RunOnBeforeUnload()
      {
         if (g_blnCheckUnload)
         {
            window.event.returnValue = 'Text, který bude přidán do confirmačního dialogu';
         }
      }
      function bypassCheck()
      { 
         g_blnCheckUnload  = false; 
      }
      function setupCheck()
      {
         g_blnCheckUnload  = true; 
      }
      registerEvents()
      {
         for (i = 0; i < document.forms[0].elements.length; i++)
         {
            document.forms[0].elements[i].onchange = setupCheck;
         }
      }
   </script>
</head>
<body onLoad="registerEvents();" onBeforeUnload="RunOnBeforeUnload();">
   <form ...>
      <input .../>
      ...
   </form>
   <a href="http://www.havit.cz">dotaz zobrazen, jsou-li změny</a>
   <a href="http://www.havit.eu" onClick="bypassCheck">dotaz nezobrazen</a>
</body>
</html>

…další vylepšování je samozřejmě možné.

Update (PetrF): Pokud nějaké existující události onChange chceme zachovat

function registerEvents()
{
 for (i = 0; i < document.forms[0].elements.length; i++)
 {
   var elem = document.forms[0].elements[i];
   var fnOnChangeOld = (elem.onchange) ? elem.onchange : function () {};
   elem.onchange = function () { fnOnChangeOld(); setupCheck() };
 }
}

…nebo přes jQuery.

SessionPageStatePersister: Ukládání ViewState do Session

ViewState se standardně ukládá do formulářového hidden input-fieldu do stránky, posílá se tedy sem a tam na klienta (GET) a zpět na server (POST).

Díky mechanizmu page-adapterů a připraveného SessionPageStatePersister-u lze v ASP.NET velmi snadno přesměrovat ukládání ViewState do Session, tedy na stranu serveru. Primárně je tento mechanizmus určený pro použití se zařízeními (browsery), kde není možné nebo žádoucí view-state pomocí hidden-fieldu přenášet (PDA, mobily, atp.). Nic nám však nebrání rozšířit jeho využití na všechny stránky.

Potřebujeme pouze dvě věci:

1. Připravit page-adapter, který používá SessionPageStatePersister

Vysvětlovat mechanizmus control-adapterů je mimo rozsah tohoto článku – je to prostě něco, co dokáže poměrně dost modifikovat výchozí chování controlů (a stránka je control), například zajistit jiné renderování, nebo právě způsob ukládání ViewState.

Potřebný page-adapter bude vypadat takto:

public class PageAdapter : System.Web.UI.Adapters.PageAdapter
{
   public override PageStatePersister GetStatePersister()
   {
      return new SessionPageStatePersister(this.Page);
   }
}

…nejde o nic jiného, než říci, že se má použít připravený SessionPageStatePersister.

2. Aplikovat page-adapter pomocí .browser souboru

ASP.NET řekneme, že má příslušný page-adapter použít, tak, že modifikujeme/vytvoříme příslušný .browser soubor. Modifikovat lze buď globální nastavení ve složce <windir>\Microsoft.NET\Framework\<ver>\CONFIG\Browsers, nebo pro jednotlivou aplikaci vytvořit .browser soubor do složky ~/App_Browsers.

Soubor ~/App_Browsers/My.browser bude vypadat třeba takto:

<browsers>
   <browser refID="Default">
      <controlAdapters>
         <adapter controlType="System.Web.UI.Page" adapterType="MyNamespace.PageAdapter"/>
      </controlAdapters>
   </browser>
</browsers>

…to je prakticky vše, co musíme udělat pro ukládání ViewState do Session.

Alternativa – overrride Page.PageStatePersister

Pokud máme ve svém projektu zavedenu společnou bázovou třídu všech stránek (což každopádně i jinak doporučuji), pal můžeme místo PageAdapteru můžeme i rovnou overridování property PageStatePersister:

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

…nevýhodou je pevné zakomponování volby Session jako ViewState uložiště do aplikace, na rozdíl od předchozí konfigurační možnosti nad hotovou aplikací.

POZOR!!! Od property PageStatePersister se nedokumentovaně očekává, že ji bude během jednoho requestu možno volat opakovaně a stále bude vracet stejnou instanci! Zatímco metoda PageAdapter.GetStatePersister() je zvnějšku obalena cachováním instance, v property si tento mechanizmus musíme zajistit sami pomocí private fieldu.

Konfigurace SessionPageStatePersisteru

SessionPageStatePersister se dá konfigurovat z web.configu pomocí elementu <sessionPageState />:

<system.web>
  <sessionPageState historySize="9" />
</system.web>

Jediným nastavitelným atributem je historySize, kterým se volí počet ViewState záznamů, které má persister udržovat. Výchozí hodnota je 9.

Úskalí použití SessionPageStatePersisteru

  • ViewState je ukládán jako položky Session + existuje slovník, který udržuje jaké klíče v Session odpovídají jakému požadavku.
  • Výchozí velikost tohoto slovníku je 9 záznamů, lze však změnit pomocí konfigurace.
  • Každý požadavek vytvoří nový záznam, desátý požadavek vytěsní první.
  • Pokud si tedy uživatel otevře více než 9 oken, pak načtení view-state selhává!!! Metoda není tedy ve výchozím nastavení příliš vhodná pro stránky s frames, nebo různá dialogová okna.
  • Ztrátou Session ztratíme i ViewState, pokud tedy máme například InProc session a restartujeme webovou aplikaci, ViewState je pryč.

Východiskem z některých situací je detekce ztráty ViewState.

Podivné chování session, přegenerovávání SessionID

V aplikaci je přihlašovací dialog. Zde by měla aplikace vytvořit session a poslat její identifikátor klientovi, což se stane.

Po přihlášení se zobrazí stránka obsahující frames, každý frame (a iframe) však znovu dostává jiný identifikátor session. To je špatné, protože každému frame je potom poslána jiný identifikátor session a tudíž má jeden uživatel více session.

Problém nenastane, pokud do přihlašovacího dialogu umístím kód:

Session["some_key"] = "some_value";
Session.Clear();

Trochu mě překvapuje, že session Session.Clear() je možné provést a je možné mít tedy prázdnou session. ASP.NET tedy pravděpodobně ruší session, pokud s ní nebylo pracováno (nebylo k ní přistoupeno).

Celý problém jsem vyřešil potomkem HttpApplication (Global.asax):

private void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
    // tento kod opravuje podivne chovani Session.
    if (Context.Session != null)
    {
        Context.Session[&quot;__Application_PreRequestHandlerExecute&quot;] = &quot;some_value&quot;;
        Context.Session.Remove(&quot;__Application_PreRequestHandlerExecute&quot;);
    }
}

IIS6: Volba výchozích regionálních nastavení pro ASP stránky

Můžeme se dočkat nepříjemností, pokud v ASP stránkách spoléháme na konkrétní regional-settings. Přesuneme-li aplikaci na server jiné lokalizace, nepomůže nám totiž ani nastavení Control Panel ~ Regional Settings (Ovládací panely ~ Regionální nastavení).

Je v podstatě několik možností, jak správný region vnutit, nicméně na IIS5.1+ mi jako nejlepší přijdezměna hodnoty AspLCID v IIS metabázi:

\\LM\W3SVC\AspLCID

Pro české regionální nastavení je správná hodnota 1029, hodnoty ostatních uvádí Microsoftí List of Locale ID (LCID).

Související zdroje informací:

Propojení databázového uživatele na login (sp_change_users_login)

Při přesunech databází mezi servery, obnovování ze záloh a podobných úkonech se nám může stát, že se ztratí propojení mezi databázovým uživatelem (User) a jeho loginem (SQL Server login). Pomocí běžných management-nástrojů pak nelze toto propojení obnovit.

Propojení obnovíme pomocí stored procedury sp_change_users_login:

USE mydb
  
-- Auto_Fix, pokud mají user i login stejné jméno, pokud login není, bude vytvořen
-- můžeme přidat i parametr @Password, který se použije, pokud bude login zakládán nově
EXEC sp_change_users_login @Action='Auto_Fix', @UserNamePattern='user'
  
-- Update_One použijeme, pokud se nám jména neshodují
EXEC sp_change_users_login @Action='Update_One', @UserNamePattern='user', @LoginName='username'

…tuto metodu nelze použít pro Windows-loginy, pouze pro SQL Server loginy.

msdtc -resetlog řeší 99% problémů s Distributed Transaction Coordinatorem (MS DTC)

Pokud nejde spustit služba Distributed Transaction Coordinator (MS DTC), bez které ostatně nefunguje skoro nic pořádně, pak mi osobně v 99% případech pomůže jednoduchý fígl z příkazové řádky

msdtc -resetlog

…jak je již zřejmé, vyresetuje se tím log MS DTC (což mj. zlikviduje všechny pending transakce). Problém je obvykle vyřešen.

Ostatně jedna z nepříjemných hlášek IIS „Server Application Error“, kdy je v event logu cosi ve smyslu „The server failed to load application ‚/LM/W3SVC/1/ROOT‘. The error was ‚Class not registered‘.“ se řeší právě takto.

adprep na Windows 2003 Server R2

Ve všech postupech Microsoftu je krásně popsáno, jak se má použít utilita adprep pro přípravu Active Directory na Windows 2000 Serveru pro použití s Windows 2003 Serverem (ať už před upgrade nebo před prostým přidáním dalšího DC).

Všechny návody však jaksi pomíjí, že na disku 2 Windows 2003 Serveru R2 je nová verze této utility a pokud použijeme tu z disku 1, o které všechny návody píšou, tak DC nepřidáme…