środa, 30 maja 2012

Id autoinkrementowanego pola w bazie

Zdarza się często, iż musimy pobrać id ostatnio wstawionego wiersza w tabeli. Dla różnych serwerów bazodanowych, pobranie tej wartości następuje w inny sposób.
Swoją drogą szkoda że w SQLu nie ma jednej standaryzowanej metody pobierania id wstawionego rekordu. Przez to niestety musimy dla każdej bazy konstruować inny rodzaj inserta.

ORACLE.
Tutaj sprawa jest bardzo prosta i wszystko właściwie załatwia jedno słowo kluczowe - returning:


Code:
INSET INTO Table(Id, FirstName) VALUES(MySeq.nextval, 'Marek') RETURNING Id INTO @id


MS SQL
Tutaj sprawa jest pozornie bardziej skomplikowana. Nie ma takiego wygodnego sposobu jak w Oracle zwrotu wartości pola po wykonanym Insercie.
Funkcją, która zwraca Id jest tutaj SCOPE_IDENTITY(). Tak więc po każdym insercie należy zawołać tę funkcję a ona zwróci numer. Należy pamiętać o jednym bardzo ważnym aspekcie: instrukcja insert i później select muszą być wykonane inline, czyli jedna po drugiej, w przeciwnym razie zamiast oczekiwanego id otrzymamy nulla:


Code:
INSET INTO Table(Id, FirstName) VALUES(MySeq.nextval, 'Marek') RETURNING Id INTO @id

Kolejne bazy będą dodawane wraz z zapotrzebowaniem na zapytania :)
A może macie już gotowe instrukcje pobierania id? Czekam na propozycje w komentarzach.


SQLITE
W tej bazie znam dwa sposoby na znalezienie ostatnio wstawianej wartości. Jeżeli w tabeli istnieje pole autoinkrementowane możemy skorzystać z instrukcji:


Code:
SELECT seq FROM sqlite_sequence WHERE name='TableName'

MS SQL: cycles or multiple cascade paths

Często możemy spotkać się z błędem który brzmi w następujący sposób:

Msg 1785, Level 16, State 0, Line 55
Introducing FOREIGN KEY constraint 'FK_Comments_Users' on table 'Comments' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Msg 1750, Level 16, State 0, Line 55
Could not create constraint. See previous errors.
Co jest powodem tego błędu?
Najłatwiej rozważyć prostą sytuację:



Rozważać właściwie będziemy tylko trzy tabele Users, Articles oraz Comments.
Z diagramu wynika że użytkownik może mieć wiele komentarzy jak i artykułów. Artykuł może mieć wiele komentarzy i jednego autora. Tworząc klucze obce wydawało by się, że prawidłowym rozwiązaniem byłoby stworzenie następujących kluczy:

ALTER TABLE dbo.Articles
  
ADD CONSTRAINT FK_Articles_Users FOREIGN KEY(UserId)
  
REFERENCES dbo.Users(Id)
  
ON DELETE CASCADE
   ON UPDATE
NO ACTION;ALTER TABLE dbo.Comments
  
ADD CONSTRAINT FK_Comments_Articles FOREIGN KEY(ArticleId)
  
REFERENCES dbo.Articles(Id)
  
ON DELETE CASCADE
   ON UPDATE
NO ACTION;ALTER TABLE dbo.Comments
  
ADD CONSTRAINT FK_Comments_Users FOREIGN KEY(UserId)
  
REFERENCES dbo.Users(Id)
  
ON DELETE CASCADE
   ON UPDATE
NO ACTION;


Ostatni klucz jest tutaj kluczowy. Wydaje się logiczne, że jeżeli usuwam użytkownika, powinny zostać usunięte jego komentarze. Zapominamy o jednym. W momencie kiedy usuwamy użytkownika, są usuwane jego artykuły a następnie kaskadowo komentarze do nich. Nie ma potrzeby usuwania ich w sposób kaskadowy jeszcze z poziomu użytkownika.

Tak więc powyższe klucze powinny mieć postać:

ALTER TABLE dbo.Articles
  
ADD CONSTRAINT FK_Articles_Users FOREIGN KEY(UserId)
  
REFERENCES dbo.Users(Id)
  
ON DELETE CASCADE
   ON UPDATE
NO ACTION;
ALTER TABLE dbo.Comments
  
ADD CONSTRAINT FK_Comments_Articles FOREIGN KEY(ArticleId)
  
REFERENCES dbo.Articles(Id)
  
ON DELETE CASCADE
   ON UPDATE
NO ACTION;
ALTER TABLE dbo.Comments
  
ADD CONSTRAINT FK_Comments_Users FOREIGN KEY(UserId)
  
REFERENCES dbo.Users(Id)
  
ON DELETE NO ACTION
  
ON UPDATE NO ACTION;


Pytanie jeszcze może budzić operacja NO ACTION w przypadku instrukcji UPDATE. Nie jest ona tutaj wymagana. Dlaczego? Ponieważ nie przewiduje w aplikacji możliwości zmiany klucza głównego osoby - są to auto numerowane pola i nie będą zmieniane w przyszłości, gdyż wprowadzić mogłoby to więcej chaosu niż pożytku.

Skrypt procedury w bazie MSSQL

Dziś szybki post.
Czasami potrzebujemy pobrać skrypt procedury zawartej już w MSSQL. Jeżeli z jakichś powodów nie da się tego zrobić z poziomu GUI można skorzystać z słowa kluczowego sp_helptex

Przykładowe użycie:
W bazie mamy procedurę GetAllComments, aby pobrać kod za pomocą którego została utworzona, wystarczy użyć następującej składni:

EXECUTE sp_helptext GetAllComments 

Wywołanie tego polecenia SQL da następujący rezultat:



wtorek, 29 maja 2012

Pobranie zawartości zasobów (resources)

Często zasoby są trzymane w plikach zasobów tzw. Resources.
Pliki te są wkompilowywane w aplikację a dodatkowo łatwo dostępne w aplikacji.
Dzisiaj potrzebowałem otrzymać listę zawierającą kolekcję wszystkich elementów zawartych w pliku Resource. Kod który umożliwia taką operację znajduje się poniżej:


Code:
        public static List<string> GetAllResourceKeys()
        {
            var resourceKeys = new List<string>();
            var resourceSet = Resources.ResourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
            foreach (DictionaryEntry key in resourceSet)
            {
                resourceKeys.Add(key.Key.ToString());
            }
            
            return resourceKeys;
        }

sobota, 26 maja 2012

Oracle - stworzenie nowej bazy danych

Bazy Oracle raczej nikomu nie trzeba przedstawić.

W Oracle nie tworzy się stricte bazy danych, a użytkownika który jest powiązany z obiektami występującymi w bazie danych (tabelami, widokami, sekwencjami itd).

Aby utworzyć nowego użytkownika w Oracle postępujemy w następujący sposób:
Za pomocą narzędzia Sql Command Line wprowadzamy kolejno polecenia
connect
system
<password> - hasło które nałożyliśmy podczas instalacji oracl-a
SQL> create user nazwa_uzytkownika identified by password;
SQL> grant connect, resources to nazwa_uzytwkonika;
SQL> exit;

W taki sposób stworzyliśmy użytkownika do którego następnie możemy się połączyć i tworzyć obiekty bazo danowe. 

 

sobota, 12 maja 2012

70-511 Rozdział 3 - Graficzny interfejs użytwkonika - Pędzle

WPF oferuje całkowicie nowe podejście w kwestii tworzenia wizualnego interfejsu użytkownika. Dzięki wykorzystaniu możliwości DirectX można tworzyć dużo bardziej intuicyjny i przyjazny interfejs.

Pędzle
Podstawowy obiekt służący do malowania interfejsu użytkonika. Można go przypisywać do różnych właściwości kontrolki jak np.
  • Background - kolor tła kontrolki
  • BorderBrush - kolor ramki kontrolki
  • Fill - kolor wypełnienia kontrolki
  • Foreground - kolor pierwszego planu, np. wartość tekstowa na przycisku
  • OpacityMask - służy do tworzenia przeźroczystych kolorów
  • Stroke - służy do malowania rogów kontrolki
 Pędzla można użyć aż do momentu gdy nie zostanie wywołana metoda Freeze. Wtedy pędzel staje się tylko do odczytu.

SolidColorBrush
Najprostszy z pędzli - tworzy jednokolorowy pędzel. Dostępne są gotowe predefiniowane pędzle w klasie Brushes:

Code:
Brush aBrush;
aBrush = Brushes.AliceBlue;

Można także podawać kolor heksadecymalne:

Code:
<Button Background="#FFFF0000"></Button>

bądź jako składowe RGB:

Code:
        <Button>
            <Button.Background>
                <SolidColorBrush>
                    <SolidColorBrush.Color>
                        <Color A="255" R="0" G="0" B="255"/>
                    </SolidColorBrush.Color>
                </SolidColorBrush>
            </Button.Background>
        </Button>




Gradient
Pozwala na tworzenie pędzla złożonego z dwóch lub więcej kolorów. Koordynaty przebiegają tak jak jest to przedstawione na poniższym obrazku:

są one relatywne do wielkości obrazka.

Code:
        <Grid>
            <Grid.Background>
                <LinearGradientBrush>
                    <GradientStop Color="Black" Offset="0"/>
                    <GradientStop Color="White" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
        </Grid>

Każdy pędzel tego typu udostępnia kolekcję GradientStop która udostępnia dwie ważne właściwości:
  • Color - kolor
  • Offset - określa miejsce w którym dany kolor nie jest mieszany z innym. 
W przykładzie powyższym górny róg będzie koloru białego, a dolny czarnego, pozostałe miejsce będzie wypełnione mieszaniną tych kolorów
Domyślnie tworzony jest gradient przebiegający w poprzek - od lewego górnego rogu do prawego dolnego. Możemy to zachowanie zmienić ustawiając dwie właściwości: StartPoint oraz EndPoint

Code:
        <Grid>
            <Grid.Background>
                <LinearGradientBrush StartPoint="0,1" EndPoint="1,1">
                    <GradientStop Color="Black" Offset="0"/>
                    <GradientStop Color="White" Offset="1"/>
                </LinearGradientBrush>
            </Grid.Background>
        </Grid>

Jeżeli EndPoint lub StartPoint nie pokrywa całej powierzchni, właściwość Spread decyduje jak będzie pokryta reszta malowanego obiektu. Spred może przyjąć jedną z 3 możliwych wartości:
  • Pad - używa jednorodnego koloru na końcach przedziału
  • Reflect - daje efekt odbitego gradientu na pozostałej powierzchni
  • Repeat - powtarza gradient


RadialGradientBrush
Tworzy gradient w kształcie kół:


Właściwości:
  • GradientOrigin - określa w którym miejscu jest środek gradientu
  • RadiusX - poziomy promień najbardziej zewnętrznego okręgu
  • RadiusY - pionowy promień najbardziej zewnętrznego okręgu

Code:
        <Grid>
            <Grid.Background>
                <RadialGradientBrush Center=".5, .5" RadiusX="0.5" RadiusY=".25">
                    <GradientStop Color="Black" Offset="0"/>
                    <GradientStop Color="BlueViolet" Offset="0.5"/>
                    <GradientStop Color="DarkCyan" Offset="0.5"/>
                    <GradientStop Color="White" Offset="1"/>    
                </RadialGradientBrush>
            </Grid.Background>
        </Grid>



ImageBrush
Pozwala malować po obiektach grafiką pobraną z pliku graficznego:

Code:
        <Grid.Background>
            <ImageBrush ImageSource="Penguins.jpg" Viewbox="0,0,1,1" />
        </Grid.Background>

Za pomocą właściwości Viewbox określamy która część obrazka ma zostać pobrana (koordynaty tak samo jak w przypadku GradientBrush)

Jeżeli właściwość Stretch jest ustawiona na None można zmienić zachowanie pędzla przez ustawienie właściwości TileMode najlepiej przedstawia poniższy obrazek:




VisualBrush
VisualBrush jest bardzo podobny w działaniu do ImageBrush z tym że do malowania używa obiektów które dziedziczą po klasie Visual np. przycisku:

Code:
<VisualBrush Visual="{Binding ElementName=Button1}">
</VisualBrush>

środa, 9 maja 2012

70-511 Rozdział 2 - Zdarzenia i komendy - animacja

Animacje w WPF pozwalają na zmianę wartości właściwości obiektów w określonym czasie. Można zmieniać kolor, rozmiar, położenie albo jakąkolwiek inną właściwość związaną z daną kontrolką.

Animację można podzielić na 3 grupy
  • Liniowe (linear) - pozwalają na zmianę w liniowy sposób danej właściwości. Nazwy rozpoczynają się od <TypeName>Animation np. DoubleAnimation, przedrostek TypeName jest nazwą typu animacji. 
  • Klatkowe (frame) - animacja jest zdefiniowana jako osobne klatki pojawiające się w zdefiniowanym czasie i mające zdefiniowaną wartość. Nazewnictwo: <TypeName>AnimationUsingKeyFrames np. StringAnimationUsingKeyFrames
  • "Ścieżkowe" - (Path based) - tłumaczenie polskie niezbyt dobre jednak chodzi o definiowanie animacji za pomocą obiektu typu Path. Najczęściej używane w przypadku zmiany położenia obiektu na formatce. Nazewnictwo: <TypeName>AnimationUsingPath np. PointAnimationUsingPath

Podstawowe właściwości, które można znaleźć w klasach odpowiedzialnych za animacje:
  • AccelerationRatio - określa jak w czasie zostanie przyśpieszona zmiana wartości animowanej właściwości
  • AutoReverse - określa czy animacja ma być odtwarzana od tyłu po jej zakończeniu
  • BeginTime - określa relatywny czas rozpoczęcia animacji. Dla przykładu: ustawienie tej właściwości na wartość 0:0:5 spowoduje że jej odtwarzanie nastąpi po 5 sekundach od jej uaktywnienia
  • DecelerationRatio - określa w jakim tempie animacja będzie zwalniała podczas wracania do punktu startowego
  • Duration - czas trwania animacji
  • FillBehavior - określa co ma się stać po zakończeniu animacji
  • RepeatBehavior - określa sposób powtarzania animacji
  • SpeedRatio - określa stosunek w jakim dana animacja wykonuje postęp w stosunku do jej rodzica
  • From - określa wartość początkową - jeżeli pominiemy będzie użyta aktualna
  • To - wartość końcowa
  • By - określa czas przez który dana właściwość będzie zmieniana - jeżeli jest ustawiona właściwość To, By jest pomijane


Storyboard
Steruje i kontroluje animacją. W właściwości Children przechowuje obiekty typu Timeline, które zawierają w sobie obiekty Animacji.
Deklaracja:


Code:
        <Storyboard TargetName="Button" TargetProperty="Height">
            <DoubleAnimation Duration="0:0:10" From="1" To="200" />
        </Storyboard>

Jako że TargetName oraz TargetProperty są właściwościami typu attached można także przypiąć je do odpowiedniego elementu:


Code:
        <Storyboard>
            <DoubleAnimation Duration="0:0:10" From="1" To="200" Storyboard.TargetName="Button" Storyboard.TargetProperty="Height" />
        </Storyboard>

Jest to preferowane rozwiązanie, gdyż w większości przypadków StoryBoard będzie zawierał więcej niż jedną animację na raz.


Użycie animacji wraz z triggerami
Aby animacja mogła być uruchomiona można użyć np. triggerów. Ich użycie może być na jeden z dwóch sposobów:
  • style
  • kolekcja triggers kontrolki
Przykład użycia jako dodanie do kolekcji triggerów kontrolki:


Code:
        <Button VerticalAlignment="Top" HorizontalAlignment="Left" Content="Przycisk" 
                Name="Button">
            <Button.Triggers>
                <EventTrigger RoutedEvent="Button.Click">
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Duration="0:0:10" From="1" To="200" Storyboard.TargetName="Button" Storyboard.TargetProperty="Height" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Button.Triggers>
        </Button>

Klasy akcji które możemy użyć w celu kontroli animacji:
  • BeginStoryboard - służy do rozpoczynania animacji
  • PauseStoryboard - zatrzymuje odtwarzanie animacji
  • ResumeStoryboard - wznawia animację
  • SeekStoryboard - szybkie przewinięcie do żądanego czasu
  • SetStoryboardSpeedRatio - ustawia szybkość odtwarzania animacji
  • SkipStoryboardToFill - przewija do końca animacji
  • StopStoryboard - zatrzymuje odtwarzanie
Przykład poniższy pokazuje w jaki sposób zatrzymać animację:


Code:
        <Style.Triggers>
            <EventTrigger RoutedEvent="Button.MouseEnter">
                <EventTrigger.Actions>
                    <BeginStoryboard Name="stb1">
                        <Storyboard>
                            <DoubleAnimation Duration="0:0:5" Storyboard.TargetProperty="Height" To="200" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
            <EventTrigger RoutedEvent="Button.MouseLeave">
                <EventTrigger.Actions>
                    <StopStoryboard BeginStoryboardName="stb1" />
                </EventTrigger.Actions>
            </EventTrigger>
        </Style.Triggers>

Ważne, aby wszystkie akcje dla jednego StoryBoard były zawarte w jednej kolekcji StoryBoard. Jeżeli będą w różnych, funkcje nie będą wywoływane poprawnie.

Aby działanie SeekStoryboard mogło mieć miejsce, należy ustawić dla niej dwie dodatkowe właściwości:
  • Orgin - może mieć wartość BeginTime lub Duration.
  • Offset - o ile zostanie przewinięta animacja od momentu Orgin


Code:
        <Style.Triggers>
            <EventTrigger RoutedEvent="Button.MouseEnter">
                <EventTrigger.Actions>
                    <BeginStoryboard Name="stb1">
                        <Storyboard>
                            <DoubleAnimation Duration="0:0:10" Storyboard.TargetProperty="Height" To="200" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
            <EventTrigger RoutedEvent="Button.MouseLeave">
                <EventTrigger.Actions>
                    <SeekStoryboard BeginStoryboardName="stb1" Origin="BeginTime" Offset="0:0:5" />
                </EventTrigger.Actions>
            </EventTrigger>
        </Style.Triggers>

Powyższy przykład powoduje przewinięcie animacji 5 sekund od aktualnej pozycji.



Użycie property triggerów
Animację można także używać w przypadku property triggerów. Dostępne są w tym przypadku dwie kolekcje:
  • EnterActions - akcje wykonywane w momencie zadziałania triggera
  • ExitActions - uruchamiane w momencie gdy trigger staje się nieaktywny 
Przykład:


Code:
        <Trigger Property="IsMouseOver" Value="True">
            <Trigger.EnterActions>
                <BeginStoryboard Name="stb1">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="FontSize" To="20" Duration="0:0:.5" />
                    </Storyboard>
                </BeginStoryboard>
            </Trigger.EnterActions>
            <Trigger.ExitActions>
                <StopStoryboard BeginStoryboardName="stb1" />
            </Trigger.ExitActions>
        </Trigger>



Klatkowe animacje
Jak wcześniej wspominałem ten rodzaj animacji definiuje serię klatek. Każda z nich posiada osobno definicję czasu trwania. Do dyspozycji mamy trzy rodzaje klatkowych animacji:
LinearDoubleKeyFrame - przejście między wartościami następuje w sposób jednostajny


Code:
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Height">
            <LinearDoubleKeyFrame Value="10" KeyTime="0:0:1" />
            <LinearDoubleKeyFrame Value="100" KeyTime="0:0:2" />
            <LinearDoubleKeyFrame Value="30" KeyTime="0:0:4"/>
        </DoubleAnimationUsingKeyFrames>

DiscreteStringKeyFrame - dla niektórych typów danych np. ciągów znakowych nie można zwiększyć wartości, dlatego stosuje się "dyskretne" zmiany, np:


Code:
        <StringAnimationUsingKeyFrames Storyboard.TargetProperty="Content">
            <DiscreteStringKeyFrame Value="Soup" KeyTime="0:0:0" />
            <DiscreteStringKeyFrame Value="Sous" KeyTime="0:0:1" />
            <DiscreteStringKeyFrame Value="Sots" KeyTime="0:0:2" />
            <DiscreteStringKeyFrame Value="Nots" KeyTime="0:0:3" />
            <DiscreteStringKeyFrame Value="Nuts" KeyTime="0:0:4" />
        </StringAnimationUsingKeyFrames>

Spline key frames - zmiany są wyrażone przy użyciu krzywej Beziera:


Code:
<SplineDoubleKeyFrame Value="300" KeyTime="0:0:6" KeySpline="0.1,0.8 0.6,0.6" />