Author Archives: Robert Haken

avatar Neznámé

About Robert Haken

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

mstsc.exe /admin

Pokud jste u RDP clienta byli zvyklí na přepínač /console, pak vězte, že ho Microsoft přejmenoval na přepínač /admin:

mstsc.exe /admin

Cílovou adresu je možné předat přepínačem /v

mstsc.exe /v:my.server.com

Je to zjednodušeně řečeno připojení na jednu vyhrazenou session RDP (přesněji řečeno lokální konzoli, jako u běžného Remote Desktopu), kde se zejména neuplatňuje limit 3 admin-session, nýbrž se v rámci té jedné vyhrazené session připojení navzájem vykopávají (a tedy je to řešení, když vás server nechce pustit na RDP kvůli limitu 3 a Vám by jinak nezbývalo, než vyrazit k serveru a otevřené session sestřelit). Potřeba je pochopitelně účet s administrátorskými právy.

Exchange 2007: Podivná authentizace starších klientů (Outlook 2003, Outlook Express)

Vynikající chybu jsem objevil v Outlook Expressu (z Win2k3 Serveru). Při připojení na standardní instalaci Exchange 2007 se při zaškrtnutí „Přihlašovat k serveru odchozí pošty“ – „Použít stejné přihlašovací údaje jako server příchozí pošty“ – klient neauthentizuje vůči serveru. Pokud však využijeme možnosti zadat authentizační údaje ručně, OE se přihlásí a poštu odešle. Pokud opět přepnete zpět na „Použít stejné jako příchozí“, tak odesílání pošty funguje, ale jen než restartujete OE.

Pominu-li tento bug v Outlook Expressu, tak problém je v tom, že standardní instalace Exchange 2007 nemá povolenu Basic authentization, resp. ji má pod podmínkou „Offer Basic Authentization only after starting TLS“. Exchange server tak v odpovědi na EHLO odpovídá jen AUTH NTLM a nenabízí AUTH LOGIN.

AUTH LOGIN povolíme odšrktnutím „Offer Basic Authentization only after starting TLS“ (Server Configuration – Hub Transport – Receive Connector – Properties – Authentication).

Exchange 2007 Setup: Topology discovery failed, error 0x80040a02 (DSC_E_NO_SUITABLE_CDC).

Pokud budete během instalace Exchange 2007 obdařeni touto príma chybou, vězte, že je potřeba zapnout IPv6 a nastavit serveru IPv6 adresu.

Podrobnosti zde.

Pozor na FileUpload v UpdatePanelu

Po prázdninové odmlce jsem tu zpět s další čerstvou zkušeností, kterou jsme udělali na jednom z našich projektů.

Story

Společné uživatelské rozhraní pro vytváření nových a editaci stávajících záznamů mělo části, které byly použitelné jen pro existující objekty, mj. i možnost připojení souborů pres control FileUpload. Části použitelné jen u existujících objektů byly skryty pomocí Visible=“fase“ a zobrazovány pomocí UpdatePanelu, který je zobrazil po uložení nového objektu.

Problém byl v tom, že u nově vytvořených objektů nebylo možné připojit soubory, zatímco u existujících objektů to bez problémů šlo. Ukázalo se, ze control FileUpload v postbacku žádný soubor nedostane (HasFile bylo false), přestože uživatel soubor do formuláře zadal. Stejně se to chovalo ve všech prohlížečích a Fiddler potvrdil, že se z klienta žádný soubor nepřenesl.

Primitivní testovací kód by mohl vypadat třeba takto:

<form id="form1" runat="server">
     <asp:ScriptManager EnablePartialRendering="false" runat="server" />
        <asp:UpdatePanel runat="server">
            <ContentTemplate>
                <asp:FileUpload ID="FileFU" Visible="false" runat="server" />
                <asp:Label ID="HasFileLb" runat="server" />
                <asp:Button ID="SaveBt" Text="Save" runat="server" />
            </ContentTemplate>
        </asp:UpdatePanel>
</form>
void SaveBt_Click(object sender, EventArgs e)
{
    FileFU.Visible = true;
    HasFileLb.Text = FileFU.HasFile.ToString();
}

Po chvilce ladění se ukázala příčina problému. V režimu editace existujícího objektu formulář již od prvního requestu renderoval control FileUpload, který si sám do elementu form doplňuje potřebný atribut enctype=“multipart/form-data“, zatímco v režimu založení nového objektu se control FileUpload renderoval až AJAXem z UpdatePanelu po uložení nového objektu. UpdatePanel však v DOM stránky vymění jen svoji část a element form zůstává nedotčen, bez atributu enctype.

Summary

Pozor na to, ze AJAXovy partial rendering pomoci UpdatePaneliu vymění pouze určitou část DOM stránky a nesmi se zapomínat na vztahy teto části se zbytkem stránky. Většinou jsou tyto vztahy zřejmé, záludnost s FileUpload a atributem enctype vsak může pozlobit.

Možným řešením je například nastavování hodnoty atributu z kódu už při prvním requestu, přestože control FileUpload ještě na stránce není:

this.Page.Form.Enctype = "multipart/form-data"

Detailní ASP.NET Request + WebForm Page LifeCycle diagram

Pokud se zabýváte technologií ASP.NET do hloubky, může se Vám hodit můj „ASP.NET 2.0/3.5 Request + Page LifeCycle Diagram“:

ASP.NET LifeCycle 2

Jedná se o první verzi mého diagramu, který hodlám dále graficky vylepšovat a zpřehledňovat. Až mi zbyde chvilka, dám sem i PDF verzi k tisku. Stejnětak je možné, že jsem v něm někde udělal chybu. Pokud tedy nějakou nesrovnalost objevíte, dejte mi prosím vědět.

Červeně jsou označena místa, kde se lze zapojit s vlastní invencí, jde buď o události, virtuální metody, nebo adapter. Modře jsou vyznačeny interface pro implementaci dané funkčnosti a šedě jsou interní implementace ASP.NET.

Viz též

SQL Transakce + mýty – Slides, dema, záznam [SQL DevCon 2008]

Slides a dema z přednášky na konferenci SQL DevCon 2008:

Z přednášky byl pořizován záznam, který najdete na našem YouTube Channelu:

AjaxControlToolkit HoverMenuExtender – dočítání vyskakovacího obsahu AJAXem

Objevil jsem jednu nepříliš dokumentovanou vlastnost HoverMenuExtenderu z AjaxControlToolkitu – že umí „vyskakovací“ obsah dočítat pomocí AJAXového callbacku na server. Nastavení je snadné, použijí se property DynamicXyz a jen je potřeba vědět (což se ukázalo jako největší kámen úrazu), jak je to vlastně celé zamýšleno a jak má vypadat serverová metoda (WebService), která má dynamický content vracet:

&lt;asp:Label ID=&quot;TargetLb&quot; Text=&quot;Ukažte sem, já se dočtu a vyskočím!&quot; runat=&quot;server&quot; /&gt;
&lt;asp:Panel ID=&quot;PopupPanel&quot; Style=&quot;display: none;&quot; runat=&quot;server&quot;&gt; &lt;%-- display:none - aby se při načítání nepřesýpala obrazovka --%&gt;
    Statický obsah pop-upu.
    &lt;asp:Panel ID=&quot;DynamicPopupContent&quot; runat=&quot;server&quot; /&gt;
&lt;/asp:Panel&gt;
&lt;ajaxToolkit:HoverMenuExtender
    TargetControlID=&quot;TargetLb&quot;
    PopupControlID=&quot;PopupPanel&quot;
    DynamicServicePath=&quot;~/AjaxServices/MyService.asmx&quot;
    DynamicServiceMethod=&quot;GetPopupContent&quot;
    DynamicContextKey=&quot;Kontext, např. ID záznamu&quot;
    DynamicControlID=&quot;DynamicPopupContent&quot;
    runat=&quot;server&quot;
/&gt;

a služba musím mít signaturu „string DoSomething(string contextKey)“:

[WebService]
[ScriptService]
public class Sluzby : System.Web.Services.WebService
{
    [WebMethod]
    [ScriptMethod]
    public string GetPopupContext(string contextKey)
    {
        return &quot;Hello World &quot; + contextKey;
    }
}

Tip 1: Pokud má být celý pop-up tvořen jen dynamickým obsahem, můžete DynamicControlID nastavit na stejný control jako PopupControlID a nemusíte pak vnořovat žádný další Panel (nebo jiný control).

Tip 2: DynamicControlID nemusí být uvnitř PopupControlID, dynamický obsah můžete dočítat i do jiného místa stránky, i když to asi není moc běžné.

Tip 3: Metodu vracející dynamický obsah můžete umístit i přímo do stránky jako PageMethod, musí být pak statická a HoverMenuExtenderu se pak nenastavuje vlastnost DynamicServicePath.

Uložení schématu databáze do SQL skriptu, z GUI i příkazové řádky

Občas se Vám může hodit možnost vyskriptovat kompletní databázové schéma do SQL skriptu. Spuštěním takového skriptu pak můžete úplné DB schema zpětně reakonstruovat. Pokud jste si (ne)oblíbili schopnosti databázové edice Visual Studia stejně jako já, pak Vás potěším, že to jde i mnohem jednodušeji pomocí tzv. SQL Publishing Wizzardu přímo ze sady standardních management  nástrojů SQL Serveru 2005:

Vyskriptování schématu DB z GUI

  1. Spusťte si SQL Server 2005 Management Studio
  2. V Object Exploreru pravým tlačítkem na dotčenou databázi a zvolte Tasks ~ Generate Scripts…
  3. Dole zaškrtněte „Script all objects in the selected database“ (v horní části okna máte předvolenu dotčenou DB), pokračujte Next,
  4. Options – Podle své potřeby můžete upravit nastavení generátoru a ovlivnit podobu výsledného skriptu, např. zvolit jinou cílovou verzi SQL Serveru, pokračujte Next,
  5. Script Mode – zvolte, kam chcete skript vygenerovat – obvykle do souboru, pokračujte Next,
  6. Review nastavení + Finish

…a je hotovo.

Vyskriptování schématu DB z příkazové řádky

…není nic jednoduššího:

"C:\Program Files\Microsoft SQL Server\90\Tools\Publishing\1.2\sqlpubwiz" script -d DbName -S ServerName -U UserName -P Password TargetScriptFile.sql -schemaonly -f

U nás tímto způsobem průběžně ukládáme ke každé solution aktuální schéma DB, tak, aby bylo v Subversion (source-control) vždy uložena příslušná verze schématu DB, s kterou aplikace pracuje.

Pro úplnost dodávám, že pro synchronizaci databázových schémat a generování rozdílových skriptů používám RedGate SQL Compare.

Update pro MSSQL 2012 (1.10.2013)

Poslední mně známá verze SqlPubWiz je 1.4 s podporou MSSQL 2008. Je tuším navíc součástí Visual Studia 2010, možná dokonce SP1. Každopádně součástí MSSQL 2012 již nic takového není, resp. z GUI je stále volba „Generate Scripts…“, která však otevře úplně nový „Generate and Publish Scripts“ wizzard. Z příkazové řádky přímá alternativa není a je potřeba použít příslušných SMO objektů (nejspíš není problém to vyskriptovat pomocí PowerShellu, nebo jednoduchou utilitou, jejíž zdrojový kód je naznačen třeba zde: http://social.msdn.microsoft.com/Forums/sqlserver/en-US/afc5dd35-0c4d-496a-a0a0-7b6aa3ef2941/command-line-database-scripting-in-2012).

Na druhou stranu, pokud jsou vaše databáze strukturálně zpětně kompatibilní s SQL 2008 (nejde o fyzický přepínač Compatibility Level, ale o skutečné použití), pak není důvod SqlPubWiz nepoužívat. My to tak stále děláme…

Na lokále mi .NET aplikace chodí, ale při spouštění ze síťové cesty padá na SecurityException

Problém, který poprvé zaskočí snad každého .NET programátora. Ani já nebyl kdysi výjimkou a tuto otázku dostávám stále dokola.

Symptom je jednoduchý – při spouštění .NET aplikace z lokálního počítače vše krásně funguje, při spouštění přes síť (např. po deploymentu do sdílené složky) aplikace padá na SecurityException.

Jádrem celého problému je .NETí mechanizmus Code Access Security Policy, který má mj. chránit hostující počítač před neoprávněným spouštěním kódu, popř. před závadným kódem. Stručně řečeno tento mechanizmus používá pro řízení práv k provádění jednotlivých operací sadu faktů zvanou Evidence (důkazy), přičemž jeden z podstatných faktů je zóna, z které je kód spouštěn (jde o klasické zóny Můj počítač, Lokální intranet, Internet, Důveryhodné servery, …). No a výchozí nastavení na hostujícím počítači po instalací .NET frameworku říká, že kód spouštěný ze zón Intranet/Internet má určitá omezení, např. nesmí přistupovat k souborovému systému, či nesmí pracovat se sítí (SQL serverem), atp.

V podstatě jsou dvě cesty k řešení:

  1. Rozšířit sadu Evidence aplikace tak, aby ve výchozím nastavení .NET frameworku měla aplikace práva lepší. Základem je např. podepsat aplikaci (strong-name), atp.
  2. Změnit výchozí nastavení zabezpečení .NET frameworku na hostujícím počítači tak, aby i aplikace z méně bezpečných zón směly provádět zabezpečené operace. Což provedeme:
    1. Spustíme MMC konzoli pro konfiguraci .NET Frameworku (z Nástrojů pro správu, popř. z Run mmc.exe a pak Přidat modul snap-in…)
    2. Donavigujeme se ve stromu na My Computer ~ Runtime Security Policty
    3. dáme Adjust Zone Security
    4. Make changes to this computer / to the current user only (dle potřeby)
    5. Vybereme zónu a šoupátkem zvýšíme její „trust“. Full Trust znamená „bez omezení“.

Pozor, neměňte bezhlavě tato nastavení. Týkají se totiž i kódu spouštěného z internetu, např. i přímo z webových stránek. Můžete tak nechtěně otevřít bránu do svého počítače i pro nežádoucí kód.

Správná cesta k řešení vede přes bod 1), nicméně připouštím, že pro začínajícího programátora a jednoduché síťové utilitky je to cesta složitější.

VasekB doplnil

lze to povolit i takto:

%SystemRoot%\Microsoft.NET\Framework\v1.1.4322\caspol.exe -q -f -cg 1.2 FullTrust

nastaveni se ulozi tady:

%SystemRoot%\Microsoft.NET\Framework\v1.1.4322\CONFIG\security.config

Jak podepsat tu aplikaci?

Musíte si nejprve vytvořit podpisový klíč, obvykle pomocí utility SN, spusťte „sn -k my.key“. Potom tento klíč připojíte ke své assembly pomocí atributu [assembly: AssemblyKeyFile(„C:\Path\my.key“)].  Obvykle se tyto atributy zapisují do souboru AssemblyInfo.cs, který lze navíc editovat pomocí UI VisualStudia (vlastnosti projektu).

Detekce ztráty ViewState při použití SessionPageStatePersisteru

Pokud směřujete ViewState do Session pomocí SessionPageStatePersisteru, pak se dříve nebo později setkáte s problémem ztrát ViewState a s potřebou detekovat a řešit tuto situaci.

Ke ztrátě ViewState může dojít v následujících případech:

  • v případě zániku/vyčištění Session, zejména při jejím timeoutování při nečinosti uživatele – uživatel si například nechá dlouho otevřenou stránku, vyprší mu mezi tím session, následně provede postback a problém je na světě, došlý postback nemá na serveru už ViewState,
  • v případě ztráty InProc Session díky restartování aplikace (deployment nové verze, recyklace poolu, atp.)
  • v případě vypadnutí stránky z fronty SessionPageStatePersisteru – persister omezuje totiž počet uložených ViewState a při uložení dalšího nejstarší vypadávají. Výchozí délka fronty je 9, je možné ji přenastavit na větší ve web.configu elementem sessionPageState, atributem historySize
    • pokud si například uživatel otevře současně 10 oken aplikace, pak pokud se vrátí k prvnímu oknu a provede postback, opět chybí ViewState,
    • pokud uživatel používá tlačítko Zpět browseru, např. pokud máme na stránce grid a každý řádek má na sobě tlačítko Detail, jehož obsluha události dělá redirect na stránku s detaily. Takový uživatel pokud klikne na detail prvního řádku, pak Zpět, druhého řádku, Zpět, … kliknutím na detail desátého řádku už způsobený postback na serveru nemá svůj ViewState (devět ViewState detailů vytlačilo z fronty ViewState stránky s gridem) a problém je zase na světě.

Jak takovou situaci detekovat? …overridováním metody LoadPageStateFromPersistenceMedium() v našich stránkách (obvykle pomocí společného předka všech stránek, nějakého PageBase):

protected override object LoadPageStateFromPersistenceMedium()
{
    // base implementace vrací new Pair(pageStatePersister.ControlState, pageStatePersister.ViewState);
    object pageState = base.LoadPageStateFromPersistenceMedium();

    // ztrátu ViewState můžeme tedy detekovat jako (pair.Second == null)
    // metoda LoadPageStateFromPersistenceMedium se volá jen pro IsPostBack
    Pair pair = pageState as Pair;
    if ((pair != null) && (pair.Second == null))
    {
        Trace.Warn("ViewStateLost !!!");

        FormsAuthentication.RedirectToLoginPage();
    }

    return pageState;
}

Základní (base) implementace metody LoadPageStateFromPersistenceMedium() totiž nedělá celkem nic jiného, než že z použitého persisteru získá ControlState, ViewState a udělá z nich Pair.

Situaci můžeme řešit například tím, že uživatele požádáme o nové přihlášení, či provedeme redirect kamsi do rootu nebo sami na sebe. ViewState už nikde nevydolujeme, musíme z toho nějak vybruslit, aniž bychom uživatele obšťastnili výjimkou NullReferenceException, InvalidFormatException a jinými podobnými, které v takových situacích nastávají.