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.

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í.

Pozor na Response.Redirect(), Response.End() a obsluhu výjimek

Jak myslíte, že dopadne následující příklad po kliknutí na tlačítko? (Stránka obsahuje jen label MyLabel a button MyButton)

public partial class _Default : System.Web.UI.Page 
{
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        MyLabel.Text = (string)Session["OK"];
    }

    void MyButton_Click(object sender, EventArgs e)
    {
        try
        {
            Session["OK"] = "ok";
            Response.Redirect("~/");
        }
        catch
        {
            Session["OK"] = "exception";
        }
    }
}

Mnohé z Vás asi překvapím, když řeknu, že do Session[„OK“] se uloží „exception“ a ten se v dalším requestu i zobrazí.

Response.Redirect(), resp. metoda Response.End(), kterou Redirect sám volá, totiž funguje tak, že vyvolá ve webové aplikaci interní výjimku (Thread.CurrentThread.Abort()), která je samotnou webovou aplikací zpracovávána tak, aby bylo dosaženo kýženého efektu, tj. aby se vykonávání kódu zastavilo v daném místě a další kód se nevykonal (resp. tato ThreadAbortException se na konci catch-bloků vyvolává znovu a jsou vykonány všechny příslušné catch/finally bloky a tedy např. i Page.OnUnload()).

Potíž však nastane v okamžiku, kdy sami obalíme volání Response.Redirect() či Response.End() zachytáváním výjimek a nespecifikujeme dostatečně typ výjimek, které chceme zachytávat. Pokud necháme chytat výjimky všechny, uvedeme jako typ Exception, pak se dočkáme nežádoucího efektu, kdy nám volání Response.Redirect()/End() způsobí vykonání obsluhy výjimky, blok catch.

Východiskem je tedy obsluhovat pouze specifické typy výjimek, tak, jak to ostatně obecné guidelines doporučují pro všechny situace.

Přenastavení počítadla pro identity sloupec (Identity Seed, RESEED)

Přenastavení počítadla pro identity sloupec na 1:

DBCC CHECKIDENT (MyTable, RESEED, 1)

Počítadlo vyresetuje na výchozí hodnotu i TRUNCATE TABLE, který slouží pro vymazání celé tabulky.

Více o DBCC CHECKIDENT [MSDN].

Roman Krejčí update:

1) syntaxe příkazu v dokumentaci je přesně

DBCC CHECKIDENT ('MyTable', RESEED, new_reseed_value)

to jest nazev tabulky by měl být v uvozovkách. Uvozovky jsou však vyžadovány jen pokud je název tabuky „multipart“, to jest je tvaru katalog.dbo.table_name. Pokud je název uveden jako single-part (bez kvalifikace katalogem a vlastníkem), lze uvozovky vynechat (ale lze je i nevynechat).

2) Hodnota new_reseed_value se použije takto – pokud od vytvoření tabulky do provedení příkazu DBCC do ní nebyl vložen žádný záznam, bude mít první vložený záznam v IDENTITY sloupci hodnotu přímo new_reseed_value. Pokud tabulka už nějaké záznamy obsahuje, bude IDENTITY sloupec v dalším přidaném záznamu obsahovat hodnotu (new_reseed_value + identity_increment), kde identity_increment je inkrement počítadla zadaný při vytvoření tabulky (lze zjistit jako select IDENT_INCR(TABLE_NAME))

Visual Studio Gallery – katalog doplňků pro VS

Microsoft připravil nový web, Visual Studio Gallery (http://visualstudiogallery.com), který má být katalogem všech možných doplňků pro Visual Studio, extenzí, add-inů, maker a podobných vylepšení. Zaregistrovat se můžete i se svými doplňky.

Znali jste například následující zajímavé bezplatné nástroje?

JavaScript: Metoda parseInt vrací nečekané výsledky

Metoda parseInt vrací celočíselnou hodnotu získanou z řetězce. Metoda toho umí ve skutečnosti více, než je od ní očekáváno – pokud řetězec začíná „0x“, považuje se řetězec za zápis čísla v šestnáckové soustavě, pokud začíná nulou, pak za zápis čísla v osmičkové soustavě. Druhým (nepovinným) parametrem můžeme metodě říct, v jaké číselné soustavě je hodnota uvedena. Pokud metoda při zpracování řetězce narazí na nepodporovaný symbol (znak), končí zpracování (což je v dokumentaci popsáno uvedeno jen v příkladu).

parseInt('abc')             // vrací NaN
parseInt('8abc')           // vrací 8 - zpracování skončí u nepodporovaného symbolu "a"
parseInt('0100')      // vrací 64 - nula na začátku říká, že jde o osmičkovou soustavu, v osmičkové soustavě je "100" reprezentací hodnoty 64
parseInt('08')               // vrací 0 - nula na začátku říká, že jde o osmičkovou soustavu, ta ale nepoužívá symbol 8, na kterém proto skončí zpracování
parseInt('08', 10)       // vrací 8 - uvedli jsme, že hodnota reprezentuje číslo v desítkové soustavě nepodporovanou mezerou
parseInt('1 000', 10) // vrací 1 - zpracování skončí

Pozor proto na zpracování hodnot od uživatele. Pokud není jasné, jak uživatel hodnotu zadá (a to podle mě není jasné nikdy), je potřeba předat metodě parseInt i druhý parameter, jinak se dočkáte stejného překvapení jako já při zpracování času zadaného uživatelem: Vstup „07“ vrátí 7, vstup „08“ vrátí 0.
Pozor i na situaci, kdy uživatel zadá formátované číslo – například s oddělovačem tisíců. A takový javascript nemusíme ani psát ručně, stačí použít některý z validátorů (ASP.NET), který používá parseInt.

IE: Při použití auto-complete se nevyvolá událost onChange

Internet Explorer při použití auto-complete nevyvolá na daném prvku (inputu) událost onChange, bug (i když někteří by možná řekli by-design).

Mimo obskurnějších řešení se dá auto-complete na daném prvku prostě vypnout:

<input ... autocomplete="off" />
<asp:TextBox ... autocomplete="off" />

Případně ho vypnout na celém formuláři:

<form ... autocomplete="off">

…nefunguje bohužel možnost na celém formuláři vypnout a uvnitř na některých prvcích zapnout.