czwartek, 29 listopada 2012

[SQL|ORM] Entity Framework : Entity Data Model - podstawy

Jednym z najważniejszych narzędzi w Entity Framework jest ADO .NET Entity Data Model.

Podstawowym jego zadaniem jest enkapsulacja logiki dostępu do danych, ale to nie wszystko. Przy użyciu EDM możemy z jednej strony tworzyć własne, relacyjne tabele, a z drugiej strony mapować już stworzone tabele na obiekty. Zatem odpowiednie korzystanie z niego spowoduje przełożenie relacji bazy danych na relacje obiektów, które dowolna aplikacja może w łatwy sposób modyfikować. Dzięki takiemu podejściu programiści nie muszą skupiać się na sposobie dostępu do danych, a mogą skoncentrować się na zarządzaniu samymi danymi.

Tworzenie własnych, prostych modeli

Aby stworzyć własny model, należy po dodaniu EDM do projektu wybrać opcję "Empty Model". Zostajemy przeniesieni do okna projektowania, gdzie możemy za pomocą menu dostępnego pod prawym klawiszem myszy dodać własne encje danych. W ten sam sposób możemy modyfikować właściwości, które zostaną zmapowane na odpowiednie kolumny bazy danych. Na property z kluczem głównym można także ustawić StoreGeneratedPattern na Identity, co spowoduje mapowanie na kolumnę z automatycznie generowanymi wartościami.

Po wybraniu opcji Generate Database from Script...( z menu dostępnego pod prawym przyciskiem myszy na powierzchni z encjami), możemy podać kilka opcji związanych z samym połączeniem, a następnie otrzymamy gotowy skrypt generujący odpowiednie tabele.


Tworzenie modelu z istniejącej bazy danych

Po dodaniu do projektu EDM, wybieramy opcję tworzenia modelu z istniejących tabel. Po podaniu connection string i wybraniu odpowiedniej bazy danych, możemy wybrać, które tabele, widoki i procedury chcemy zaimportować jako modele. Warto zaznaczyć opcję pluralizing, która spowoduje, że kontenery na nasze obiekty będą miały nazwy będące liczbami mnogimi od nazw tabel. Poza mapowaniem tabel i kolumn na obiekty i propercje, przeniesione zostaną również relacje między tabelami na relacje między obiektami.

Po poprawnym mapowaniu, można dodawać nowe dane do tabel poprzez encje w następujący sposób. 

using (var context = new EntityFrameworkRecipesEntities1())
{
    Poet p = new Poet()
                 {
                     FirstName = "Wiliam",
                     LastName = "Shakespeare",
                 };
    p.PoetId = 1;
    Poem poem = new Poem()
                    {
                        Title = "Hamlet"
                    };
    poem.Poet = p;
    poem.PoemId = 1;
    context.Poems.AddObject(poem);
    context.SaveChanges();
}

Co spowoduje dodanie po jednym rekordzie do obu tabel, połączonych kluczem obcym

czwartek, 15 listopada 2012

.NET Framework

Wszystko na temat Visual Studio, języka C# i technologii opartych na platformie .NET Framework:

wtorek, 13 listopada 2012

[C#|Visual Studio] C# Syntactic Sugars

Syntactic sugar to konstrukcja składniowa, charakterystyczna dla danego języka programowania, która ułatwia wykonanie pewnych operacji i poprawia czytelność. W C# aż roi się od specyficznych syntactic sugars, charakterystycznych tylko dla tego języka. Przykładem mogą być LINQ, które z jednej strony w minimalistyczny sposób pozwalają na operacje na kolekcjach, a z drugiej strony poprawiają czytelność.

W tym poście przedstawione zostaną niektóre, mniej popularne konstrukcje C#, które warto znać i które często mogą ułatwić życie.
  • default - słowo kluczowe, które zwraca wartość domyślną dla każdego typu (zawsze null dla typów referencyjnych, dla ValueTypes wartości te zdefiniowane są w specyfikacji języka

    public class defaultKeyword
    {
        public static T GetDefaultValue<T>()
        {
            return default(T);
        }   
    }

Console.WriteLine(defaultKeyword.GetDefaultValue<int>());
Console.WriteLine(defaultKeyword.GetDefaultValue<DateTime>());
//zwroci null, wypisze pusty string
Console.WriteLine(defaultKeyword.GetDefaultValue<defaultKeyword>());
  • volatile - obiekt może być modyfikowany przez wiele wątków jednocześnie. Obiekt taki nie jest poddawany optymalizacji kompilatora, który domyślnie zakłada, że dostęp do obiektu z pojedynczego wątku
  • implicit generics - kompilator w przypadku metod generycznych może wywnioskować typ po argumencie
public static void Print<T>(T instance)
{
    Console.WriteLine(instance);
}

//implicit generics
implicitGenerics.Print(new ASCIIEncoding());
//nie trzeba pisac
//implicitGenerics.Print<ASCIIEncoding>(new ASCIIEncoding());
  • @ - pozwala używać słów kluczowych C# w miejscach, w których normalnie jest to niedozwolone, np jako nazwy zmiennych

//@ character
string @string = "string";
Console.WriteLine(@string);
  • checked - używany do kontroli działań arytmetycznych, w których może nastąpić przepełnienie, gdy operację oznaczymy jako checked i przepełnienie wystąpi, rzucony zostanie wyjątek, gdy oznaczymy ją jako unchecked, wartość zostanie wyliczona przy uwzględnieniu "przekręcenia licznika"

//checked unchecked
short x = 32767;   // 32767 is the max value for short
short y = 32767;
try
{
    int z1 = checked((short)(x + y));   //will throw an OverflowException
    Console.WriteLine(z1);
}
catch (Exception e)
{
    Console.WriteLine(e.Message);
}

int z2 = unchecked((short)(x + y)); // will return -2
Console.WriteLine(z2);
  • static constructor - wywoływany automatycznie raz, przed tym, gdy pierwsza instancja jest utworzona, bądź przed stworzeniem pierwszej referencji do statycznego pola / propercji, gdy żadna z tych okoliczności nie zajdzie, konstruktor nie zostanie wywołany
  • params - umożliwia wywoływanie funkcji ze zmienną liczbą argumentów tego samego typu
public class paramsKeyword
{
    public static void Sum(params int[] values)
    {
        Console.WriteLine(values.Sum());
    }
}
//params
Console.WriteLine("params");
paramsKeyword.Sum(1,2,3);
paramsKeyword.Sum(1,2,3,4,5);
nnbvn

poniedziałek, 12 listopada 2012

[C#|Visual Studio] Struktury

Często niedocenianym przez wielu programistów .NET typem danych są struktury. Ich stosowanie zalecane jest wszędzie, gdzie mamy do czynienia z lekkimi, często stosowanymi obiektami, jak na przykład współrzędne, figura geometryczna, pole na szachownicy itd. Korzystanie z dużych kolekcji obiektów definiowanych jako klasy powoduje o wiele większe zużycie pamięci, niż gdybyśmy zdefiniowali te obiekty jako struktury. Poniżej kilka faktów z życia struktur w świecie .NET
  • może zawierać pola, metody, konstruktory
  • nie może dziedziczyć żadnego typu (poza ValueType, z którego dziedziczą niejawnie), może implementować interfejsy
  • w MSIL dodawane jest słowo kluczowe sealed, przez co nie można ze struktur dziedziczyć
  • nie mogą zawierać jawnego bezparametrowego konstruktora
  • nie muszą być inicjowane operatore new, np.
    public struct MyStruct
    {
        public int x;
        public int y;
    }

MyStruct b;
b.x = 3;
b.y = 2;
Console.WriteLine(b.x);
Console.WriteLine(b.y);
  • pól nie można inicjalizować w ciele struktury, a jedynie w konstruktorze zawierającym parametry, lub indywidualnie można nadawać wartość każdemu polu w kodzie proceduralnym
  • aby korzystać ze struktur jako typów referencyjnych należy przeprowadzić operację boxing, aby powrócić do ValueType, należy przeprowadzić unboxing

//boxing
object o = b;
//unboxing
b = (MyStruct) o;
  • boxing zachodzi również podczas rzutowania do interfejsów, z których dziedziczy dana struktura
  • obiekty klas powstają na stercie (w pamięci wolnej), przez co sprzątane są przez Garbage Collector w sposób niederministyczny, struktury natomiast tworzone są na stosie, przez co sprzątane są zaraz po wyjściu z bloku kodu, np po wykonaniu funkcji, w której je deklarujemy
  • inicjowanie instancji struktur operatorem new nie powoduje przeniesienia ich na stertę
  • jako argumenty funkcji możemy przekazywać struktury za pomocą sowa kluczowego ref

piątek, 9 listopada 2012

[C#|Visual Studio] Partial methods

Partial classes to konstrukcja składniowa, umożliwiająca definiowanie jednej klasy w wielu plikach. Może to być przydatne, by rozdzielić prace nad jedną klasą na kilku developerów, lub by odseparować np. kod generowany od kodu pisanego przez programistę, np. WinForms. Kompilator zbiera wszystkie pliki w całość i kompiluje jako jedną klasę. Wymagane jest, by pliki znajdowały się w jednym assembly..

O ile idea partial classes wydaje się w miarę jasna, o tyle temat partial methods może wielu programistom wydawać się niejasny.

Kilka informacji na temat partial methods
  • mogą znajdować się tylko w partial classes lub partial structs
  • jedna część klasy zawiera jedynie sygnaturę, w drugiej zawarta jest definicja
  • definicja metody jest opcjonalna (w przypadku jej braku kompilator pominie wszystkie wywołania)
  • dzięki powyższym cechą mogą być luźnie stosowane do wprowadzania opcjonalnych funkcjonalności
  • definicja musi rozpoczynać się od słowa kluczowego partial, a metoda może zwracać jedynie void
  • są to metody prywatne (niejawnie)
  • mogą być statyczne, generyczne
Przykład użycia:

Pierwsza część definicji klasy (np. wygenerowana przez automat)


using System;

namespace partial_methods
{
    //simulates automatically generated code
    public partial class ClassAB
    {
        //implemented
        partial void PrintYourName();
        //not implemented
        partial void PrintSurname();

        public void Print()
        {
            Console.WriteLine("Hello");
            PrintYourName();
            PrintSurname();
        }
    }
}

Druga część (np. definiowana przez użytkownika)


using System;

namespace partial_methods
{
    //simulates user defined class
    public partial class ClassAB
    {
        private string _name;

        public ClassAB(string s)
        {
            _name = s;
        }
        partial void PrintYourName()
        {
            Console.WriteLine(_name);
        }
    }
}

czwartek, 8 listopada 2012

[C#|Visual Studio] Konwersja typów

Nieco zapomnianymi i nieczęsto używanymi słowami kluczowymi C# są explicit i implicit. Służą one do zdefiniowania odpowiednio jawnej i niejawnej konwersji typów. Korzystanie z nich we własnych projektach może często zmniejszyć ilość kodu, poprawić jego przejrzystość. Gdy kod taki wystawiamy jednak jako API, programista może mieć problem z uzyskaniem informacji na temat tego, czy dane dwa typy są do siebie konwertowalne. 

explicit
  • wykonuje się tylko przy rzutowaniu newObject = (newType)object
  • gdy operator nie jest zdefiniowany, kompilator nie dopuści do konwersji
  • definiowany jako public static explicit operator

public class Celsius
{
    public float Value { get; set; }

    public static explicit operator Celsius(float v)
    {
        Console.WriteLine("explicit float -> Celsius");
        return new Celsius() { Value = v };
    }    
}

class Program
{
    static void Main(string[] args)
    {
        int deg_int = 20;

        Console.WriteLine();
        //explicit conversion
        Console.WriteLine("Celsius temp_Celsius =  (Celsius)deg_int;");
        Celsius temp_Celsius =  (Celsius)deg_int;
    }
}

implicit
  • operator niejawnej konersji, np przy przypisaniu elementu jednego typu do drugiego, przy wywołaniu metod itd.
  • kompilator nie dopuści do konwersji gdy nie zdefiniowaliśmy operatora
  • definiowany jako public static explicit operator

public class Celsius
{
    public float Value { get; set; }

    public static implicit operator double(Celsius v)
    {
        Console.WriteLine("implicit Celsius -> double");
        return v.Value;
    }

    public static void Print(double d)
    {
        Console.WriteLine(d);
    }

    public static void Put(int i)
    {
        Console.WriteLine(i);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Celsius temp_Celsius =  new Celsius(){Value = 20};

        //implicit conversion
        Console.WriteLine("double deg_double = temp_Celsius");
        double deg_double = temp_Celsius;

        Console.WriteLine("Celsius.Print(temp_Celsius)");
        Celsius.Print(temp_Celsius);

        Console.ReadLine();
    }
}

środa, 7 listopada 2012

[C#|Visual Studio] ConditionalAttribute

Podczas tworzenia oprogramowania często wykorzystujemy kod, nie mający żadnego pożytku po wdrożeniu rozwiązania. Może to być na przykład kod wypisujący coś na konsoli w celu sprawdzenia poprawności wykonania programu, lub właściwości służące jedynie do testów, pomiaru szybkości wykonania, wydajności itd.

Aby uniknąć sytuacji, w której musimy przy wdrożeniu usuwać spore ilości kodu, możemy skorzystać z atrybutu warunkowego. Oznaczone w nim metody będą wołane tylko w przypadku, gdy zdefiniowane jest makro z warunku, w przeciwnym przypadku kod programu przeskoczy wywołanie, a Visual Studio wyszarzy miejsce, w którym wołana jest taka funkcja.

Niespełnienie warunku i tak spowoduje wkompilowanie metod w assembly, co można łatwo sprawdzić przez mechanizm refleksji.


using System;
using D = System.Diagnostics;

namespace ConditionalAttribute
{
    public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }

        [D.Conditional("DEVELOP")]
        public void Print()
        {
            Console.WriteLine("Person");
        }

        public void FullName()
        {
            Console.WriteLine(Name + " " + Surname);
        }
    }
}


#define DEVELOP

using System;

namespace ConditionalAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person();
            person.Name = "John";
            person.Surname = "Doe";

            var type = typeof (Person);
            foreach (var m in type.GetMethods())
            {
                Console.WriteLine(m.Name);
            }
            Console.WriteLine("");

            person.Print();
            person.FullName();

            Console.ReadLine();
        }
    }
}

wtorek, 6 listopada 2012

[C#|Visual Studio] FlagsAttribute

Flagi bitowe to popularny sposób oznaczania pewnych obiektów przez enumeration w taki sposób, że mamy do dyspozycji dostępne wszystkie możliwe kombinacje (każdy z każdym) i każdej kombinacji przypada dokładnie jedna liczba. Aby uzyskać takie efekt, wartości kolejnych pól enuma oznaczamy przez kolejne potęgi dwójki. Potęga dwójki, to w zapisie bitowym jedynka na jednym miejscu i same zera w pozostałych miejscach. Po wykonaniu logicznej operacji OR na dwóch takich liczbach, różnych od siebie dostajemy dwie jedynki i same zera, co oznacza, że dany obiekt przynależy do dwóch zbiorów jednocześnie.

 .NET Framework wspiera flagi bitowe poprzez specjalny atrybut Flags. Korzystając z niego nie tylko oznaczamy, że dany typ wyliczeniowy jest flagą bitową. Dodatkowo wypisując wartości takiego typu dostajemy wylistowane, które zbiory taka wartość reprezentuje.

Warto pamiętać o ustawieniu wartości dla zer - będzie to wartość domyślna ustawiana przy starcie aplikacji przez CLR.

    class Program
    {
        static void Main(string[] args)
        {
            var list1 = new List<Roles>();
            var list2 = new List<Jobs>();
            for (var i = 0; i < 16; i++)
            {
                list1.Add((Roles)i);
                list2.Add((Jobs)i);
            }

            Console.WriteLine("Jobs - without flag");
            foreach (var job in list2)
            {
                Console.WriteLine(job);
            }

            Console.WriteLine("Roles - with flag");
            foreach (var role in list1)
            {
                Console.WriteLine(role);
            }
            Console.ReadLine();
        }
    }

    [System.Flags]
    public enum Roles
    {
        None = 0,
        Tester = 1,
        Developer = 2,
        Architect = 4,
        Manager = 8
    }

    public enum Jobs
    {
        None = 0,
        Tester = 1,
        Developer = 2,
        Architect = 4,
        Manager = 8
    } 
}

Na konsoli wypisane zostało:

Jobs - without flag
None
Tester
Developer
3
Architect
5
6
7
Manager
9
10
11
12
13
14
15
Roles - with flag
None
Tester
Developer
Tester, Developer
Architect
Tester, Architect
Developer, Architect
Tester, Developer, Architect
Manager
Tester, Manager
Developer, Manager
Tester, Developer, Manager
Architect, Manager
Tester, Architect, Manager
Developer, Architect, Manager
Tester, Developer, Architect, Manager

poniedziałek, 5 listopada 2012

[C#|Visual Studio] ThreadStaticAttribute

Tworząc programy wielowątkowe, w których korzystamy ze zmiennych statycznych, możemy ich używać na dwa sposoby. Zmienna statyczna może być globalna i współdzielona dla wszystkich wątków, albo też statyczna w obrębie jednego wątku. W tym przypadku dla każdego wątku tworzona jest osobna zmienna statyczna. Ustawienie tej funkcjonalności odbywa się poprzez atrybut ThreadStaticAttribute.

Na poniższym przykładzie można zaobserwować różnicę przy obu przypadkach.

namespace ThreadStaticAttribute
{
    public class MyClass
    {
        [System.ThreadStatic] public static int Count;
        public static int Count2;

        public void Count10()
        {
            for (int i = 0; i < 10; i++)
            {
                Count++;
                Count2++;
            }
        }

        public void Count20()
        {
            for (int i = 0; i < 20; i++)
            {
                Count++;
                Count2++;
            }
        }
    }
}

using System;
using System.Threading;

namespace ThreadStaticAttribute
{
    class Program
    {
        static void Main(string[] args)
        {
            var c1 = new MyClass();
            Thread t1 = new Thread(new ThreadStart(c1.Count10));
            Thread t2 = new Thread(new ThreadStart(c1.Count20));
            t1.Start();
            t2.Start();
            t1.Join();
            t2.Join();
            MyClass.Count++;
            MyClass.Count2++;
            Console.WriteLine(MyClass.Count);
            Console.WriteLine(MyClass.Count2);
            Console.ReadLine();
        }
    }
}

Zgodnie z przewidywaniami, konsola wyświetli wynik, jak poniżej.

1
31

niedziela, 4 listopada 2012

[C#|Visual Studio] DebuggerBrowsableAttribute

Kolejnym atrubutem pomocnym podczas debugowania programu jest DebuggerBrowsable znajdujący się, podobnie jak DebuggerDisplay w System.Diagnostics. Atrybut ten okazuje się pomocny, gdy przeglądamy obiekty składające się z dużej ilości properties, a chcemy śledzić stan jedynie niektórych z nich, bądź wyświetlać je w sposób łatwiejszy do podglądu. Nad każdym property można umieścić atrybut DebuggerBrowsable, który w konstruktorze przyjmuje jedną z trzech opcji.
  • Never - pole lub property nie będzie widoczne po rozwinięciu obiektu w zakładce Locals
  • Collapsed - zachowanie domyślne, obiekt widoczny, ale nie rozwinięty
  • RootHidden - w przypadku wyświetlania kolekcji, jej elementy są widoczne poziom wyżej, bez wyświetlania jej roota. Np. jeśli mamy listę napisów jako property pewnej klasy, to przy ustawieniu RootHidden, wszystkie napisy będą widoczne jako properties tej klasy.
Zasadę działania ilustruje także poniższy przykład.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using D = System.Diagnostics;

namespace DebuggerBrowsableAttribute
{
    public class SampleClass
    {
        [D.DebuggerBrowsable(D.DebuggerBrowsableState.RootHidden)]
        public List<int> RootHidden { get; set; }

        [D.DebuggerBrowsable(D.DebuggerBrowsableState.Collapsed)]
        public int Collapsed { get; set; }

        [D.DebuggerBrowsable(D.DebuggerBrowsableState.Never)]
        public int Never { get; set; }

        public SampleClass()
        {
            Random r = new Random();
            RootHidden = new List<int>();
            RootHidden.Add(r.Next(100));
            RootHidden.Add(r.Next(100));
            RootHidden.Add(r.Next(100));
            Collapsed = r.Next(100);
            Never = r.Next(100);
        }
    }
}


sobota, 3 listopada 2012

[C#|Visual Studio] DebuggerDisplayAttribute

DebuggerDisplayAttribute można znaleźć w System.Diagnostics. Atrybut ten służy do kontroli sposobu, w jaki wyświetlamy daną zmienną w oknie zmiennych debuggera. Przydaje się to np. do klas zawierających kilka properties, które chcemy podejrzeć. Jako argument konstruktora podajemy string z możliwością używania wyrażeń umieszczonych w nawiasach klamrowych {}. W wyrażeniach tych można na przykład podawać nazwy properties, przeprowadzać na nich operacje, czy wywoływać funkcje. Należy jednak pamiętać, że operacja wykona się przy każdym odświeżeniu debuggera, przez co często zmusimy komputer do sporej ilości niekoniecznie potrzebnych obliczeń.

using System;
using D = System.Diagnostics;

namespace DebuggerDisplayAttribute
{
    [D.DebuggerDisplay("Sample class: First: {First}, Second: {Second}, Sum: {Sum()}")]
    public class SampleClass
    {
        public int First { get; set; }
        public int Second { get; set; }

        public SampleClass()
        {
            Random r = new Random();
            First = r.Next(100);
            Second = r.Next(100);
        }

        public int Sum()
        {
            return First + Second;
        }
    }
}

Podczas zatrzymania pracy programu w trybie debug, z automatu otrzymamy:


piątek, 2 listopada 2012

[C#|Visual Studio] DefaultValueAttribute

Jednym z atrybutów, jakie udostępnia System.ComponentModel jest DefaultValueAttribute. Wbrew pozorom podanie wartości jako argument konstruktora nie wystarczy, aby ustawić domyślną wartość property. Dokumentacja MSDN jasno mówi, że programista musi sam ustawić domyślne wartości, np. w konstruktorze. Aby wykorzystać możliwości DefaultValueAttribute można skorzystać z mechanizmu reflection.

Zatem poniższy kod, pomimo swojej przejrzystości nie wystarczy, aby ustawić domyślne wartości dla properties.

using System;
using CM = System.ComponentModel;

namespace DefaultValueAttribute
{
    public class MySimpleClass
    {
        [CM.DefaultValue(true)]
        public bool MyBoolProperty { get; set; }

        [CM.DefaultValue(8)]
        public int MyIntProperty { get; set; }

        [CM.DefaultValue("Default text")]
        public string MyStringProperty { get; set; }
    }
}

Aby móc skorzystać z możliwości ustawiania wartości domyślnych poprzez atrybuty, wystarczy skorzystać z poniższej ExtensionMethod.

public static class Extensions
    {
        public static void ApplyDefaultValues(this object obj)
        {
             foreach (CM.PropertyDescriptor prop in CM.TypeDescriptor.GetProperties(obj))
             {
                 var attr = (CM.DefaultValueAttribute) prop.Attributes[typeof (CM.DefaultValueAttribute)];
                 {
                     prop.SetValue(obj, attr.Value);
                 }
             }
        }
    }

Wywoływać ją można np. w konstruktorze.