COUNT(*)
dává počet všech řádek
COUNT(col)
dává počet řádek, které mají hodnotu sloupce col jinou než NULL
COUNT(*)
dává počet všech řádek
COUNT(col)
dává počet řádek, které mají hodnotu sloupce col jinou než NULL
Kromě klasické sp_who má MSSQL server (tuším 7.0+) ještě nedokumentovanou procedurusp_who2, která kromě základní informací z sp_who (ne všech!) přidává ještě několik dalších podrobností o aktuální uživatelské aktivitě.
Jako nedokumentovaná procedura je celkem nepoužitelná do produkčního prostředí, ale při vývoji a ladění se může hodit. Například je tam šikovný údaj BlkBy (Blocked By), který udává, na jaký jiný proces se čeká, aby se mohlo pokračovat.
Rozhraní IComparable, popř. strong-typed IComparable<T> slouží jako podpora pro porovnávání prvků, zejména pro jejich řazení (metody Sort). K implementaci je jediná instanční metoda CompareTo(), jejíž výsledek má být:
Tato konvence vyplynula z metodiky používané pro porovnávání čísel prostým odečtením this-param.
Dále musíme dodržet následující definiční pravidla, na která se často zapomíná:
Samozřejmě taky všechna klasická pravidla:
Vzorová implementace obou rozhraní by pak mohla vypadat takto:
public class MyComparableClass : IComparable, IComparable<MyComparableClass>
{
// Strong-typed implementace IComparable<T>
public int CompareTo(MyComparableClass other)
{
if (other == null)
{
return 1;
}
// naše implementace srovnání, například
return this.Value.CompareTo(other.Value);
}
// Implementace IComparable
public int CompareTo(object obj)
{
if (obj == null)
{
return 1;
}
if (obj is MyComparableClass)
{
return CompareTo(obj as MyComparableClass);
}
throw new ArgumentException("obj musí být typu MyComparableClass", "obj");
}
}
Doporučuje se, aby třída, která implementuje IComparable, overridovala i metodu Equals.
Z SQL stored procedur můžeme oznamovat vnější aplikaci chyby prostřednictvím příkazu RAISERROR, nejběžněji:
RAISERROR(message_str, severity_int, state_int, arg1, arg2)
přičemž
např. tedy
RAISERROR ('The level for job_id:%d should be between %d and %d.',
16, 1, @@JOB_ID, @@MIN_LVL, @@MAX_LVL)
A teď k efektu RAISERROR v ADO.NET – vše závisý na parametry severity. Pro uživatelské chyby jsou určeny severity od 1 do 18, severity od 19 do 25 jsou určeny pro sysadmina.
Výsledký efekt v ADO.NET je tedy následující:
POZOR!!! Samotné RAISERROR nezajistí ROLLBACK transakce (musí se provést explicitně), ani nepřeruší běh T-SQL dávky (nutno třeba RETURN).
ICloneable je rozhraní (interface) .NET Frameworku pro podporu klonování objektů, tedy pro vytváření nových instancí třídy se stejnými hodnotami, jako má instance existující.
Základním kamenem pro implementaci ICloneable je protected metoda Object.MemberwiseClone(), která vytvoří shallow-copy (mělkou kopii) objektu. Mělkost kopie spočívá v tom, že jsou zkopírovány pouze přímé hodnoty objektu, což u referenčních typů má za následek pouze vytvoření nových referencí na stejnou instanci.
Korektní implementace rozhraní ICloneable by měla vypadat asi takto:
class MyCloneableClass : System.ICloneable
{
// Explicitní implementace metody rozhraní.
// Potřebná pro klienty ICloneable, ale neviditelná
// běžné klienty MyCloneableClass
object ICloneable.Clone()
{
// prostě vrátíme hodnotu naší implementace
return this.Clone();
}
// Silně-typovaná metoda Clone
public virtual MyCloneableClass Clone()
{
// běžná memberwise-kopie vlastních hodnot
MyCloneableClass x = this.MemberwiseClone() as MyCloneableClass;
// následuje deep-copy všeho, co je potřeba
x.somethingDeep = this.somethingDeep.Clone();
return x;
}
}
Pokud ICloneable korektně implementujeme u všech našich tříd, bude vše velmi primitivní. Vlastní klonování bude probíhat v kaskádě, resp. procházením stromu do hloubky. Problém samozřejmě nastane, když budeme mít člen referenčního typu, který neimplementuje ICloneable.
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:
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).
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>
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.
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.
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;
}
}
Mozilla navigate(‚url‘) nepodporuje.
Místo toho je potřeba použít standardní
window.location.href = 'url';
…což funguje i v IE.
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"
/>
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…