Author Archives: Robert Haken

avatar Neznámé

About Robert Haken

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

SQL2005: Nepoužívat už ntext, text, image

Datové typy ntext, text a image budou z budoucích verzích Microsoft SQL Serveru odstraněny. Doporučuje se tedy místo nich používat nové typy nvarchar(MAX), varchar(MAX) avarbinary(MAX).

Rozdíl mezi int.Parse() a Convert.ToInt32()

Mnoho vývojářů tápe, kterou z metod Int32.Parse() a Convert.ToInt32() používat pro parsování textových vstupů na číslo. Rozdíl je malý, a sice v interpretaci vstupu null. Vše je jasné, pokud se podíváme na vnitřnosti Convert.ToInt32():

 

public static int ToInt32(string value)
{
   if (value == null)
   {
      return 0;
   }
   return int.Parse(value, CultureInfo.CurrentCulture);
}

Metoda Convert.ToInt32() tedy na vstup null vrátí hodnotu 0, zatímco int.Parse() vyhodí výjimku ArgumentNullException.

Vyčištění webové Cache (application cache)

Někdy se nám může hodit kompletní vyčištění webové application cache (datové cache přístupné přes HttpRuntime.Cache, Control.Cache, HttpContext.Current.Cache, …), například po změně cachovaných hodnot „read-only“ číselníků.

.NET Framework pro takovou operaci žádnou přímou pomůcku nemá, nebo o ní alespoň nevím. Jde to však obejít poměrně snadno. Třída Cache totíž implementuje rozhraní IEnumerable a metoda GetEnumerator() vrací IDictionaryEnumerator. Stačí tedy po jedné vyházet všechny položky:

 foreach (DictionaryEntry de in Cache)
{
   Cache.Remove(de.Key.ToString());
}
 

Implementováno v Havit.Web.HttpServerUtilityExt.ClearCache().

Viz též

Zobrazení controlu jen v určité položce Repeateru

Je poměrně snadný způsob, jak alespoň částečně obměňovat obsah položek repeateru. Chceme-li například zobrazit control jen v první položce (mezi první a druhou položkou), pak  takto:

<asp:Repeater ID="ZpravickyRepeater" Runat="server">
   <ItemTemplate>
      ...
      <xy:CokolivTrebaPlaceHolder Visible="<%# (Container.ItemIndex == 0) %>" Runat="server"/>
   </ItemTemplate>
</asp:Repeater>

Equals, operátory == a !=

 

Metoda Equals() slouží v .NET Frameworku k ověření rovnosti. Její výchozí implementace Object.Equals() ověřuje referenční rovnost, tedy zda obě reference odkazují na stejnou instanci. U typů, které nesou nějaké smysluplné hodnoty, může být však vhodnější implementovat Equals jako ověření hodnotové rovnosti, byť by se jednalo o dvě různé instance.

.NET má metodu Object.Equals ve dvou podobách

  • public virtual bool Equals(object obj) – k té chceme implementovat override
  • public static bool Equals(object objA, object objB)
public static bool Equals(object objA, object objB)
{
   if (objA == objB)
   {
      return true;
   }
   if ((objA != null) && (objB != null))
   {
      return objA.Equals(objB);
   }
   return false;
}

Statická metoda tedy nejprve ověří rovnost referencí, pokud jde o jednu instanci, vrátí true. Potom ověří, zda-li není jeden z objektů null. Pokud ano, vrátí false, pokud ne zavolá instanční podobu, kterou právě overridujeme.

Kontrakt

Nejprve základní pravidla, které musí Equals splňovat:

  • reflexe – A.Equals(A) = true,
  • symetrie – když A.Equals(B), pak B.Equals(A),
  • tranzitivita – když A.Equals(B) a B.Equals(C), pak i A.Equals(C),
  • konzistence – pokud nedojde ke změně objektů, pak A.Equals(B) musí dávat stále stejnou hodnotu,
  • A.Equals(null) = false,
  • metoda nesmí vyhazovat žádnou výjimku,

Doporučení:

  • potomek by měl volat base.Equals(), pokud je to možné,
  • měly bychom overridovat též GetHashCode(),
  • pokud implementujeme IComparable, měli bychom overridovat Equals(),
  • měly bychom vytvořit i strong-typed overload Equals().
Operátory ==, !=

Zároveň s override metody Equals() je vhodné přetížit i operátory == a !=, jejichž základní implementace ověřuje opět referenční shodu. Implementace je jednoduchá, prostě se odkážeme opět na statické Object.Equals()

Implementace

Ukažme si tedy běžnou implementaci pro business-object, u kterého nám stačí shoda ID.

public virtual bool Equals(BusinessObjectBase obj)
{
   if ((obj == null) || (this.GetType() != obj.GetType()))
   {
      return false;
   }
   if (!Object.Equals(this.ID, obj.ID))
   {
      return false;
   }
   return true;
}
public override bool Equals(object obj)
{
   if (obj is BusinessObjectBase)
   {
      BusinessObjectBase bob = obj as BusinessObjectBase;
      return this.Equals(bob);
   }
   return false;
}
public static bool operator ==(BusinessObjectBase objA, BusinessObjectBase objB)
{
   return Object.Equals(objA, objB);
}
public static bool operator !=(BusinessObjectBase objA, BusinessObjectBase objB)
{
   return !Object.Equals(objA, objB);
}
public override int GetHashCode()
{
   return this.ID;
}

Samotné porovnávání členských hodnot, které determinují shodu, je opět vhodné nechat na statické metodě Object.Equals().

Ještě si ukažme, jak by měla vypadat implementace potomka, který by chtěl mimo ID hlídat ještě shodu na nějakou další hodnotu (doporučené volání base.Equals())

public override Equals(Order order)
{
   return base.Equals((BusinessObjectBase)order)
            && Object.Equals(this.Cena, order.Cena);
}
public override bool Equals(object obj)
{
   if (obj is Order)
   {
      Order order = obj as Order;
      return this.Equals(order);
   }
   return false;
}
Viz též

Rozdíl mezi COUNT(col) a COUNT(*)

COUNT(*)

dává počet všech řádek

COUNT(col)

dává počet řádek, které mají hodnotu sloupce col jinou než NULL

sp_who2 – nedokumentovaná procedura MSSQL

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.

IComparable

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:

  • nula, pokud jsou instance stejné (z hlediska porovnání rovnocenné),
  • záporný, pokud je je instance (this) menší než parametr metody (při vzestupném řazení bude this dříve než parametr),
  • kladný, pokud je instance (this) větší než parametr metody (při vzestupném řazení bude parametr dříve než this).

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á:

  • null < cokoliv jiného,
  • null = null (neimplementuje se)

Samozřejmě taky všechna klasická pravidla:

  • když A = B a B = C, tak A = C,
  • když A < B a B < C, tak A < C,
  • když A < B, tak B > A,

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.

RAISERROR a jeho efekt v ADO.NET

RAISERROR

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ž

  • message_str představuje textový popis chyby v like-PRINTF podobě s procentama (argn pak představují hodnoty k dosazení),
  • severity_int udává závažnost chyby (viz níže),
  • state_int je číslo od 1 do 127 a předává se tím stavová hodnota (obvykle prostě 1)

např. tedy

RAISERROR ('The level for job_id:%d should be between %d and %d.',
      16, 1, @@JOB_ID, @@MIN_LVL, @@MAX_LVL)
ADO.NET

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

  • severity <= 10 nevyvolá výjimku v .NET, nýbrž event InfoMessage související SqlConnection (k odchycení musíme navěsit event-handler),
  • severity > 10 vyvolá výjimku SqlException, ale nezavře spojení,
  • severity > 16 vyvolá výjimky SqlException a uzavře spojení.

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

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.