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();
        }
    }
}