Indexy (vč. unikátních) v Entity Frameworku (EF6.1)

Při studiu zdrojových kódu knihovny Microsoft.AspNet.Identity.EntityFramework.dll jsem narazil na zápis, který mi připomněl novinku v Entity Frameworku 6.1 – možnost definovat entitám indexy, včetně těch unikátních.

Principiálně jde o použití data-annotation atributu [Index(„<index name>“, [isUnique: true|false])] na příslušné property, přičemž pokud je atribut se stejným názvem použit na více properties, vznikne index přes více sloupců (vlastností Order se řídí pořadí sloupců v definici indexu).

Ve FluentAPI lze takový efekt zapsat nepřímo, pomocí doplnění anotace:

this.Property(t => t.Identifier)
	.HasColumnAnnotation(
		IndexAnnotation.AnnotationName,
			new IndexAnnotation(
				new IndexAttribute("UIDX_MonitoredApplication_Identifier") { IsUnique = true }));

Viz též EntityFramework specs - IndexAttribute.

C# 6.0: Ostatní (extension-Add v initializers, overload resolution) [10/10]

Do C# 6.0 se dostaly ještě dvě „drobotiny“, které většině vývojářů život nijak zásadně nezmění.

Collection-initializers podporují extension-podobu Add() metody

V C# 5.0, v inicializaci kolekcí, nemohla být použita extension-podoba metody Add(), přestože VB.NET toto měl. Byl to spíše bug/opomenutí. Každopádně nyní je to napraveno.

Vylepšené vyhodnocování overloadů

Drobná vylepšení, kterých si málokdo všimne. O něco málo méně míst, kde se můžete setkat s výtkou kompilátoru ohledně nejednoznačnosti volby overloadu (např. u nullable hodnotových typů parametrů, atp.)

Série článků o novinkách v C# 6.0:

  1. Auto-property initializers a getter-only (read-only) auto-properties
  2. Expression-bodied function members
  3. Using static
  4. Null-conditional operators (?., ?[], …)
  5. String inerpolation – zkrácený String.Format()
  6. nameof() expressions
  7. Index initializers – přehlednější inicializace slovníků
  8. Exception filters, await v catch/finally blocích
  9. Konstruktor struct bez parametrů
  10. Ostatní (extension-Add v initializers, overload resolution)

C# 6.0: Konstruktor struct bez parametrů [9/10]

C# 5.0 nedovoluje vytvořit na struct explicitní constructor bez parametrů, hodnoty fieldů jsou totiž automaticky inicializovány svými default(T) hodnotami. C# 6.0 toto omezení odstraňuje:

struct Person
{
    public string Name { get; }
    public int Age { get; }
   
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public Person() : this("Jane Doe", 37)
    { }
}

Důležitým omezením však je, že takový konstruktor se použije pouze v případě explicitního volání new Person(), zatímco default(Person) stále založí výchozí hodnoty fieldů, stejnětak vytvoření pole new Person[..].

Série článků o novinkách v C# 6.0:

  1. Auto-property initializers a getter-only (read-only) auto-properties
  2. Expression-bodied function members
  3. Using static
  4. Null-conditional operators (?., ?[], ...)
  5. String inerpolation - zkrácený String.Format()
  6. nameof() expressions
  7. Index initializers - přehlednější inicializace slovníků
  8. Exception filters, await v catch/finally blocích
  9. Konstruktor struct bez parametrů
  10. Ostatní (extension-Add v initializers, overload resolution)

C# 6.0: Exception filters, await v catch/finally bloku [8/10]

C# 6.0 dohání chybějící drobnosti kolem výjimek

Exception filters

Catch-blok je vyvolán, pouze pokud je vedle typu výjimky splněna i podmínka:

try
{
    … 
}
catch (MyException ex) if (condition) // už v podmínce se lze odkázat na ex
{
    …
}

Znásilnit tuto konstrukci lze i pro neinvazivní logování výjimek, sám bych s tím však byl opatrný.

private static bool Log(Exception e) { /* log it */ ; return false; }
…
try { … } catch (Exception e) if (Log(e)) {}

Await v catch a finally blocích

C# 5.0 nepodporoval await v catch a finally blocích, protože se Microsoft domníval, že to není potřebné a implementace by byla neúměrně komplikovaná. Nyní dlužnou funkčnost doplnili:

Resource res = null;
try
{
    res = await Resource.OpenAsync(…);
}
catch(ResourceException e)
{
    await Resource.LogAsync(res, e);
}
finally
{
    if (res != null) await res.CloseAsync();
}

Série článků o novinkách v C# 6.0:

  1. Auto-property initializers a getter-only (read-only) auto-properties
  2. Expression-bodied function members
  3. Using static
  4. Null-conditional operators (?., ?[], ...)
  5. String inerpolation - zkrácený String.Format()
  6. nameof() expressions
  7. Index initializers - přehlednější inicializace slovníků
  8. Exception filters, await v catch/finally blocích
  9. Konstruktor struct bez parametrů
  10. Ostatní (extension-Add v initializers, overload resolution)

C# 6.0: Index initializers – přehlednější inicializace slovníků [7/10]

Jednou z předchozích novinek v C# byla možnost při založení kolekce/slovníku naplnit ho rovnou i počátečními hodnotami. C# 6.0 přichází se zpřehledněním zápisu pro inicializaci slovníků (a podobných objektů s indexery):

// C# 5.0
var numbers = new Dictionary<int, string> {
    {7, "seven"},
    {9, "nine"},
    {13, "thirteen" }
};

// C# 6.0
var numbers = new Dictionary<int, string> {
    [7] = "seven",
    [9] = "nine",
    [13] = "thirteen"
};

Série článků o novinkách v C# 6.0:

  1. Auto-property initializers a getter-only (read-only) auto-properties
  2. Expression-bodied function members
  3. Using static
  4. Null-conditional operators (?., ?[], ...)
  5. String inerpolation - zkrácený String.Format()
  6. nameof() expressions
  7. Index initializers - přehlednější inicializace slovníků
  8. Exception filters, await v catch/finally blocích
  9. Konstruktor struct bez parametrů
  10. Ostatní (extension-Add v initializers, overload resolution)

C# 6.0: nameof() expressions [6/10]

Přestože se nová FluentAPI snaží vyhnout textovým řetězcům ve prospěch „strong-type“ zápisů, stále se na mnoha a mnoha místech setkáváme s potřebou zapsat do kódu textový řetězec, který odkazuje na název nějakého prvku. Potíže s tím související jsou nasnadě – kompilátor nám takový textový řetězec nekontroluje a jakýkoliv automatický refactoring v podobě přejmenování prvku zanechá textový řetězec nedotčen. Jde tak o častý zdroj chyb.

C# 6.0 přináší pomůcku, kterou se dá těmto problémům vyhnout. Konstrukci nameof(prvek) převede kompilátor na textovou konstantu odpovídající názvu prvku:

// C# 5.0
(if x == null) throw new ArgumentNullException("x");

// C# 6.0
(if x == null) throw new ArgumentNullException(nameof(x));

Uvnitř nameof() může být prakticky cokoliv, co má jméno - proměnná, typ, metoda, property, atp. Vždy se však použije pouze poslední část jména:

Console.WriteLine(nameof(person.Address.ZipCode)); // vrátí "ZipCode"
Console.WriteLine(nameof(System.Console.WriteLine)); // vrátí "WriteLine"

Série článků o novinkách v C# 6.0:

  1. Auto-property initializers a getter-only (read-only) auto-properties
  2. Expression-bodied function members
  3. Using static
  4. Null-conditional operators (?., ?[], ...)
  5. String inerpolation - zkrácený String.Format()
  6. nameof() expressions
  7. Index initializers - přehlednější inicializace slovníků
  8. Exception filters, await v catch/finally blocích
  9. Konstruktor struct bez parametrů
  10. Ostatní (extension-Add v initializers, overload resolution)

C# 6.0: String interpolation – zkrácený String.Format() [5/10]

Velmi často používaná metoda String.Format() získala v C# 6.0 syntaktic-sugar v podobě zkráceného zápisu, který má být přehlednější a méně náchylný na chyby:

// C# 5.0
var s = String.Format("{0} is {1} year{{s}} old", p.Name, p.Age);

// C# 6.0 cílová podoba s $ na začátku
var s = $"{p.Name,20} is {p.Age:D3} year{{s}} old";

// C# 6.0 ve VS2015 Preview
var s = "\{p.Name} is \{p.Age} year{s} old";

Půjde samozřejmě použít i další formátovací instrukce, např.

var s = $"{p.Name,20} is {p.Age:D3} year{s} old";
var s = $"{p.Name} is {p.Age} year{(p.Age == 1 ? "" : "s")} old";

Série článků o novinkách v C# 6.0:

  1. Auto-property initializers a getter-only (read-only) auto-properties
  2. Expression-bodied function members
  3. Using static
  4. Null-conditional operators (?., ?[], ...)
  5. String inerpolation - zkrácený String.Format()
  6. nameof() expressions
  7. Index initializers - přehlednější inicializace slovníků
  8. Exception filters, await v catch/finally blocích
  9. Konstruktor struct bez parametrů
  10. Ostatní (extension-Add v initializers, overload resolution)