czwartek, 20 maja 2010

Autentykacja w ASP.NET

Autentyfikacja podczas pisania aplikacji WWW zawsze stwarzała dużo problemów oraz niepotrzebnie konsumowała czas. Z nowymi kontrolkami które wprowadzono w ASP.NET 2.0 staje się tak prosta jak przeciągnięcie kontrolki na formę (bo i z tym jest związana ).

A więc bez zbędnych rozwodów na temat tego jak dokładnie działa cała struktura stworzona przez Microsoft, skupimy się na sednie i zbudujemy prosty przykład autentyfikacji:

Na początek potrzebujemy bazy danych w której przechowamy dane użytkowników. Tworzymy więc w SQL Server 2008 nową bazę danych. Nazwałem ją po prostu test.
Baza jest póki co pusta i nie zawiera żadnych tabel. W naszej bazie chcemy zarządzać użytkownikami i ich rolami (np. Admin, Power user itp.). Tak wiec potrzebujemy tabel do przechowywania tych informacji. Dzięki narzędziu Aspnet_regsql wygenerujemy je bez problemu.

A więc przystępujemy do dzieła. Uruchamiamy Aspnet_regsql w trybie konsoli (większe możliwości konfiguracyjne niż w „graficznej” wersji wizarda). Standardowo aplikacja znajduje się w lokalizacji:
C:\WINDOWS\Microsoft.NET\Framework\\aspnet_regsql.exe i wprowadzamy komendę. W zależności od tego czy chcemy dodać czy usunąć różne możliwości stosujemy komendy -A (dodaj) lub -R (remove). Do wyboru mamy takie rzeczy jak:
all – wszystko
m – membership
r – role management
p – profile
c – Web parts personalization
w – Web events

Dodatkowo następującymi przełącznikami określamy takie rzeczy jak:
-S – Server np. localhost lub konkretna instancja SQL Server
-U – user id
-P – password
-E – autentyfikacja przy użyciu Windows credentials
-d – baza danych

Tak więc przystępujemy do pracy. W naszym przypadku wystarczy do bazy test dodać: membership, profile oraz role manager:




Tak więc możemy sprawdzić naszą bazę danych:



Jak widać została stworzona podstawa bazy danych. Mamy wszystko co jest niezbędne aby umożliwić autentyfikację.

Przechodzimy więc do Visual Studio i tworzymy nową aplikację internetową ASP.NET. Na początek należy zmodyfikować plik Web.config i uzupełnić go o wpisy dotyczące zarządzania rolami i użytkownikami.
Po otwarciu pliku WebConfig uzupełniamy go o wpisy (opis tego co dodaliśmy znajduje się w komentarzach):

<?xml version="1.0"?>

<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->

<configuration>
  <!--Wstawiamy Connection string do naszej bazy danych gdzie przechowywane są tabele z wymaganymi informacjami-->
  <connectionStrings>
    <add name="dbC" connectionString="Data Source=localhost;Initial Catalog=test;Integrated Security=True" providerName="System.Data.SqlClientr"/>
  </connectionStrings>

    <system.web>
        <authentication mode="Forms" />
        <compilation debug="true" targetFramework="4.0" />

      <!--Konfiguracja membership-->
      <membership defaultProvider="MySqlMembershipProvider">
        <providers>
          <!-- uzupełniamy pola: connectionStringName oraz applicationName; connectionStringName - Podajemy nazwę taką, jaką umieściliśmy w sekcji connectionString-->
          <add connectionStringName="dbC"
            enablePasswordRetrieval="false"
            enablePasswordReset="true"
            requiresQuestionAndAnswer="true"
            applicationName="/"
            requiresUniqueEmail="true"
            maxInvalidPasswordAttempts="5"
            passwordAttemptWindow="10" name="MySqlMembershipProvider"
            type="System.Web.Security.SqlMembershipProvider, System.Web,&#xD;&#xA;Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
            minRequiredPasswordLength="6"
            minRequiredNonalphanumericCharacters="0" />
        </providers>
      </membership>

      <!--Koniguracja obsługi ról-->
      <roleManager enabled="true" defaultProvider="SqlRoleProvider">
        <providers>
          <clear />
          <!--Tak jak poprzednio podajemy nazwę connectionString-->
          <add applicationName="MyAppName"
            connectionStringName="dbC"
            name="SqlRoleProvider"
            type="System.Web.Security.SqlRoleProvider" />
        </providers>
      </roleManager>

    </system.web>

</configuration>


Po tych modyfikacjach uruchamiamy narzędzie ASP.NET Configuration. Za pomocą prostego kreatora dodamy użytkownika i role.

Po uruchomieniu narzędzia przechodzimy do zakładki Security. Przechodzimy do Select authentication type. Zaznaczamy tutaj From the Internet i naciskamy Done:


Następnie klikamy na Enable roles. Dzięki temu będziemy mogli stworzyć jakieś proste role, które następnie wykorzystamy w naszej aplikacji. Klikamy więc na Create or Manage roles i dodajemy nowe role:


Po dodaniu żądanych ról, klikamy na Back. Przejdziemy teraz do tworzenia użytkowników. Wybieramy opcję Create user i wprowadzamy żądane dane:


Po wprowadzaniu danych i kliknięciu Create User po pomyślnej walidacji zostanie dodany nowy użytkownik z wybranymi prawami przez nas.

W tym momencie możemy zamknąć narzędzie. Wrócimy do niego później.

Aby możliwe było logowanie, należy stworzyć stronę przeznaczoną do tego celu. Tworzymy więc WebForms o nazwie login.aspx i modyfikujemy nasz Web.config:

      <authentication mode="Forms">
        <forms loginUrl="login.aspx"></forms>
      </authentication>

Dodając tą klauzulę, mówimy gdzie znajduje się strona pozwalająca na zalogowanie się użytkownika. Następnie na stronie umieszczamy kontrolkę Login. Kontrolka ta prosi użytkownika o wprowadzenie hasła i loginu. Pozwala także na walidację wprowadzonych przez użytkownika danych (np. jeżeli są wymagane cyfry to sprawdzi czy użytkownik wprowadził takowe). Tyle.
To wszystko co należało zrobić aby kontrolka mogła rozpocząć działanie. Oczywiście od nas zależy jej działanie, wyświetlane teksty itd.

Do wykorzystania mamy także inne kontrolki jak np. ChangePassword która umożliwia zmianę hasła użytkownikowi; CreateUserWizard – pozwala na dodawanie nowych użytkowników; LoginName – wyświetla login zalogowanego użytkownika; LoginStatus – wyświetla stan zalogowania; LoginView – pozwala na ustalenie wyświetlanej zawartości w zależności od zalogowanego użytkownika i jego praw.

Aby teraz wykorzystać stworzone role, należy stworzyć formularz logowania oraz foldery. Dla każdej z ról osobny:


Następnie przechodzimy do narzędzia ASP.NET Configuration. Wchodzimy tak jak poprzednio w opcje Security tylko tym razem będziemy zajmować się Access Rules. Klikamy na Create access rulet i z listy filderów (Select a directory for this rule) wybieramy interesujący nas folder np.


Na screenie widzimy że dla folderu Administrator i dla roli Administrator ustalamy zezwolenie na Allow (czyli administrator będzie mógł przeglądać stworzone w tym folderze strony). Dla reszty użytkowników można ustawić brak zezwolenia (Deny).

Oprócz dodawania nowych przywilejów (Create access rulet), możemy także nimi zarządzać (dodawać, usuwać, zmieniać). Dostęp do tych opcji mamy w Manage access rulet. Na screenie widać przykładowo przydzielone przez nas role.


Tyle. Teraz w zależności od roli użytkownika logującego się, zależy jaką zawartość zobaczy po prawidłowym logowaniu.

niedziela, 16 maja 2010

Composite Pattern

Wzorzec ten umożliwia organizację obiektów w strukturę drzewiastą jako związek całość - część. Dzięki temu klient traktuje w taki sam sposób pojedyncze obiekty jak i złożone struktury.

Co zyskujemy dzięki temu? W większości przypadków możemy zignorować różnicę pomiędzy agregatem obiektów a indywidualnymi obiektami.

Schemat UML tego wzorca przedstawia się następująco:


Klasy z których składa się ten wzorzec są następujące:
Component - definiuje interfejs dla wszystkich obiektów drzewa

Leaf - reprezentuje liść, czyli węzeł który nie posiada potomków (podobnie jak Composite impelementuje metody Add, Remove itp. jednak w jego przypadku są one zbędne w implementacji).

Composite - definiuje zachowanie obiektów posiadających dzieci i przechowuje do nich referencje.

Przejdźmy do przykładu:

    public abstract class CourseComponent <T>
    {
        public virtual void Add(CourseComponent<T> component)
        {
            throw new NotSupportedException();
        }

        public virtual void Remove(CourseComponent<T> component)
        {
            throw new NotSupportedException();
        }

        public virtual CourseComponent<T> GetChildren(int i)
        {
            throw new NotSupportedException();
        }

        public virtual string GetDescription()
        {
            return "";
        }
    }

    //Our Composite
    public class Course<T> : CourseComponent<T>
    {
        public string CourseName { get; set; }

        private List<CourseComponent<T>> list = new List<CourseComponent<T>>();

        public override CourseComponent<T> GetChildren(int i)
        {
            return list[i];
        }

        public override void Add(CourseComponent<T> component)
        {
            list.Add(component);
        }

        public override void Remove(CourseComponent<T> component)
        {
            list.Remove(component);
        }

        public override string GetDescription()
        {
            string s = CourseName + Environment.NewLine;
            foreach (var item in list)
            {
                s+=item.GetDescription() + Environment.NewLine;
            }
            return s;
        }
    }


    //Our leaf
    public class Student<T> : CourseComponent<T>
    {
        private string _firstName;

        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        private string _lastName;

        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }

        public override string GetDescription()
        {
            return _firstName + " " + _lastName;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Course<string> mathCourse = new Course<string>();
            mathCourse.CourseName = "MathCourse";
            Course<string> algebraCourse = new Course<string> { CourseName = "AlgebraCourse" };
            mathCourse.Add(algebraCourse);
            algebraCourse.Add(new Student<string> { FirstName = "Mateusz", LastName = "Kowalski" });
            mathCourse.Add(new Student<string> { FirstName = "Jan", LastName = "Kowalski" });

            Console.WriteLine(mathCourse.GetDescription());
        }
    }

Mamy tutaj prosty przykład wzorca, pokazującego kursy i studentów do nich należących. Dzięki takiemu wzorcowi zarówno studentów jak i kursy możemy traktować tak samo.

Co należy rozważyć podczas pracy z tym wzorcem projektowym?
Przede wszystkim należy się zastanowić czy ważna jest dla nas kolejność dodawania elementów. Jeżeli tak musimy stworzyć rozwiązanie które pozwoli nam w prosty sposób manipulować "miejscem" gdzie dodawany jest nowy element. Także należy wziąć pod uwagę to, czy struktura jest rozbudowana. Jeżeli tak, można stworzyć mechanizm caschingu, który pozwoli na przechowywanie wyników obliczeń.

Kiedy nie stosować Composite Pattern?
Przede wszystkim wtedy kiedy klient potrzebuje dostępu do pojedynczych obiektów, albo te obiekty będą udostępniały metody niedostępne dla reszty drzewa.

sobota, 15 maja 2010

Rozwiązywanie układów równań liniowych


W tym poście, zostaną omówione jedne z najczęściej spotykanych metod numerycznych dzięki którym można rozwiązać układ n równań liniowych z n niewiadomymi. Zostaną omówione metody:
  1. Eliminacja Gaussa
  2. Eliminacja Gaussa - Jordana
  3. LU
  4. Metoda Jacobiego
  5. Gauss - Seidel
Wszystkie omówione metody posiadają przykładowy kod w języku C#. Na końcu postu znajduje się aplikacja wykonana w Windows Forms pozwalająca przetestować poznane algorytmy.


1. Eliminacja Gaussa
Na początek zaczniemy od krótkiego przykładu, który pokaże w jaki sposób obliczyć dany układ tą metodą.
Mamy następujący układ równań:



Zapiszemy powyższe równanie w postaci macierzy:


Mając tak zapisaną macierz przystępujemy do eliminacji. Naszym celem jest uzyskanie macierzy:


Gdzie x jest wartością którą obliczymy. Aby doprowadzić macierz do takiej postaci najpierw eliminujemy x1 z równania drugiego. W tym celu do równania drugiego musimy dodać pierwsze pomnożone przez -2/3:


Następnie z równania trzeciego eliminujemy także x1 poprzez pomnożenie równania pierwszego przez -1/3 i dodanie do równania trzeciego:


Teraz czas na eliminację z równania trzeciego x2. Dokonujemy tego poprzez pomnożenie równania drugiego przez -1/14:


W tym momencie posiadamy tzw. Macierz trójkątną górną. Postępowanie prowadzimy od końca najpierw obliczając x3 następnie wprowadzając do równania drugiego otrzymaną wartość i obliczenie x2 itd.
Ostatecznie wynik jaki otrzymamy będzie następujący:


Przejdźmy teraz do obliczeń od strony programistycznej.
W pierwszym kroku z równania drugiego eliminowaliśmy x1. Aby tego dokonać obliczyliśmy przez co należy pomnożyć równanie pierwsze, aby po odjęciu zlikwidowało się x1 w równaniu drugim. Szukaną wartością było -a_21/a11, otrzymaną wartość mnożyliśmy przez wszystkie wyrazy równania pierwszego i dodawaliśmy bezpośrednio do drugiego:


W ogólności dla drugiego równania mamy:


W kolejnych krokach eliminujemy z równania trzeciego x1 a następnie z równania trzeciego x2. W ogólności mamy więc n – 1 kroków. Ogólnie nasz wzór na obliczenie macierzy trójkątnej górnej wygląda następująco:


Postępowanie odwrotne:
Z ostatniego równania obliczamy x3:


Następnie otrzymaną wartość wstawiamy do równania drugiego i obliczamy x2. W ogólności wzór na obliczenie xn jest następujący:


Przykładowa implementacja podanego algorytmu w języku C#:

        public double[] GaussElimination(double[,] A, double[] b, int n)
        {
            double[] x = new double[n];

            double[,] tmpA = new double[n, n + 1];

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    tmpA[i, j] = A[i, j];
                }
                tmpA[i, n] = b[i];
            }

            double tmp = 0;

            for (int k = 0; k < n - 1; k++)
            {
                for (int i = k + 1; i < n; i++)
                {
                    tmp = tmpA[i, k] / tmpA[k, k];
                    for (int j = k; j < n + 1; j++)
                    {
                         tmpA[i, j] -= tmp * tmpA[k, j];
                    }
                }
            }

            for (int k = n - 1; k >= 0; k--)
            {
                tmp = 0;
                for (int j = k + 1; j < n; j++)
                {
                    tmp += tmpA[k, j] * x[j];
                }
                x[k] = (tmpA[k, n] - tmp) / tmpA[k, k];
            }

            return x;
        }


2. Eliminacja Gaussa - Jordana
Tak samo jak w poprzednim przypadku - rozpoczniemy od przykładu rozwiązanego tą metodą:
   

Zapisujemy układ jako macierz:


Krok 1: Z równań drugiego i trzeciego usuwamy x1, w tym celu równanie pierwsze mnożymy najpierw przez -2 a następnie przez 3:


Krok 2: Z równań pierwszego i trzeciego usuwamy x2, w tym celu równanie drugie mnożymy najpierw przez 2/5, a następnie przez 4/5:


Krok 3: Równania drugie i trzecie upraszczamy poprzez podzielenie przez 5 oraz 3:


Krok 3: Z równań pierwszego i drugiego eliminujemy x3, w tym celu równanie trzecie mnożymy najpierw przez -1 a następnie przez 1:


Rozwiązaniem układu są więc wartości:


Przejdźmy teraz do części programistycznej powyższej metody. Jeżeli ktoś wcześniej zapoznał się z metodą Gaussa, to nie wiele mamy do zmian. Więc jeżeli jeszcze nie zapoznałeś się z metodą eliminacji Gaussa – możesz to zrobić teraz. Jakie zmiany musimy nanieść w metodzie Gaussa aby uzyskać metodę Gaussa-Jordana? Właściwie tylko kosmetyczne:
  • Każdy wiersz normalizujemy poprzez dzielenie go przez element główny
  • Zmienne są eliminowane ze wszystkich równań a nie tylko następnych
  • Po n krokach otrzymujemy macierz jednostkową – nie ma więc postępowania odwrotnego

Kod C#:

        public double[] GaussJordanElimination(double[,] A, double[] b, int n)
        {
            double[] x = new double[n];
            double[,] tmpA = new double[n, n + 1];
            double tmp = 0;

            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    tmpA[i, j] = A[i, j];
                }
                tmpA[i, n] = b[i];
            }

            for (int k = 0; k < n; k++)
            {
                tmp = tmpA[k, k];
                for (int i = 0; i < n + 1; i++)
                {
                    tmpA[k, i] = tmpA[k, i] / tmp;
                }

                for (int i = 0; i < n; i++)
                {
                    if (i != k)
                    {
                        tmp = tmpA[i, k] / tmpA[k, k];
                        for (int j = k; j < n + 1; j++)
                        {
                            tmpA[i, j] -= tmp * tmpA[k, j];
                        }
                    }
                }
            }

            for (int i = 0; i < n; i++)
            {
                x[i] = tmpA[i, n];
            }

            return x;
        }

3. LU

W metodzie tej macierz A przedstawiamy jako iloczyn macierzy LU. Macierz L to dolna macierz trójkątna a U górna macierz trójkątna.


Do rozkładu na powyższe macierze L i U można użyć metody Doolittle’a. W metodzie równość A=LU traktuje się jako układ n2 równań z n2 niewiadomymi. Elementy macierzy L i U są wyznaczane naprzemiennie tzn. kolumnę macierzy L a później wiersz macierzy U itd. Wzory ogólne na rozkład:


Przykład:


Zapisujemy w postaci macierzy:


W pierwszym kroku obliczamy pierwszy wiersz macierzy U:


Teraz obliczymy kolumnę macierzy L:


Teraz znów wiersz macierzy U:


Kolejno obliczymy teraz ostatnią kolumnę macierzy L a następnie ostatni wiersz macierzy U:


Ostateczna postać macierzy L i U po rozkładzie:


Rozwiązaniem układu odbywa się kolejno przez wykonanie działań:


Tak więc pierwsze to po prostu rozwiązanie macierzy trójkątnej górnej, a kolejne równanie to rozwiązanie macierzy trójkątnej górnej (jak w metodzie Gaussa).

Przykładowy kod w C# tworzący macierz L, U oraz rozwiązujący układ równań:

        public void LUDecompozition(double[,] A, double[,] L, double[,] U, int n)
        {
            for (int i = 0; i < L.GetLength(0); i++)
            {
                L[i, i] = 1;
            }


            double tmp;
            for (int i = 0; i < n; i++)
            {
                for (int j = i; j < n; j++)
                {
                    tmp = 0;
                    for (int k = 0; k < i; k++)
                    {
                        tmp += L[i, k] * U[k, j];
                    }
                    U[i, j] = A[i, j] - tmp;
                }

                for (int j = i + 1; j < n; j++)
                {
                    tmp = 0;
                    for (int k = 0; k < i; k++)
                    {
                        tmp += L[j, k] * U[k, i];
                    }
                    L[j, i] = (A[j, i] - tmp) / U[i, i];
                }
            }
        }

        public double[] SolvWithLUMethod(double[,] L, double[,] U, double[] y, int n)
        {
            double[] z = new double[n];
            double[] x = new double[n];

            z = SolvLowerMatrix(L, y, n);
            x = SolvUpperMatrix(U, z, n);

            return x;
        }

        public double[] SolvLowerMatrix(double[,] L, double[] b, int n)
        {
            double[] x = new double[n];

            x[0] = b[0] / L[0, 0];
            double tmp;
            for (int i = 1; i < n; i++)
            {
                tmp = 0;
                for (int j = 0; j < i; j++)
                {
                    tmp += L[i, j] * x[j];
                }
                x[i] = b[i] - tmp;
            }

            return x;
        }

        public double[] SolvUpperMatrix(double[,] U, double[] b, int n)
        {
            double[] x = new double[n];
            double tmp;

            for (int k = n - 1; k >= 0; k--)
            {
                tmp = 0;
                for (int j = k + 1; j < n; j++)
                {
                    tmp += U[k, j] * x[j];
                }
                x[k] = (b[k] - tmp) / U[k, k];
            }

            return x;
        }

4. Metoda Jacobiego


Metoda ta jest zaliczona do metod iteracyjnych (Gauss, Gauss-Jordan, LU to metody skończone). Metody iteracyjne nadają się do rozwiązywania bardzo dużych układów równań – np. powyżej miliona równań.

W metodzie tej tworzy się ciąg przybliżeń według wzoru:


Przykładowa implementacja w C#:

        public double[] JacobiMethod(double[,] A, double[] b, int n, double eps)
        {
            double tmp;
            double[] x = new double[n];
            double[] x1 = new double[n];
            double sumx, sumx1;
            int count = 0;
            for (int i = 0; i < n; i++)
            {
                x1[i] = b[i] / A[i, i];
            }

            do
            {
                for (int i = 0; i < n; i++)
                {
                    x[i] = x1[i];
                }

                for (int i = 0; i < n; i++)
                {
                    tmp = 0;
                    for (int j = 0; j < n; j++)
                    {
                        if (j != i)
                        {
                            tmp += A[i, j] * x[j];
                        }
                    }
                    x1[i] = (1.0 / A[i, i]) * (b[i] - tmp);
                }

                sumx = 0;
                sumx1 = 0;
                for (int i = 0; i < n; i++)
                {
                    sumx += x[i];
                    sumx1 += x1[i];
                }

                ++count;
            } while (Math.Abs(sumx - sumx1) > eps);

            return x1;
        }


5. Metoda Gaussa - Seidela

Metoda ta, jak i Jacobiego należy do metod iteracyjnych. W porównaniu do metody Jacobiego, metoda Gaussa – Seidela każda modyfikacja rozwiązania próbnego korzysta ze wszystkich dostępnych przybliżeń. Dzięki temu można uzyskać praktycznie dwukrotne zmniejszenie ilości powtórzeń w celu otrzymania zadanej dokładności w porównaniu z metodą Jacobiego.

Kolejne przybliżenia w tej metodzie oblicza się korzystając ze wzoru:


Przykładowy kod w C# :

        public double[] GaussaSeidela(double[,] A, double[] b, int n, double eps)
        {
            double tmp1;
            double tmp2;
            double[] x = new double[n];
            double[] x1 = new double[n];
            double sumx, sumx1;
            int count = 0;
            for (int i = 0; i < n; i++)
            {
                x1[i] = b[i] / A[i, i];
            }

            do
            {
                for (int i = 0; i < n; i++)
                {
                    x[i] = x1[i];
                }

                for (int i = 0; i < n; i++)
                {
                    tmp1 = 0;
                    tmp2 = 0;
                    for (int j = 0; j < i; j++)
                    {
                        tmp1 += A[i, j] * x1[j];
                    }
                    for (int j = i + 1; j < n; j++)
                    {
                        tmp2 += A[i, j] * x[j];
                    }
                    x1[i] = (1.0 / A[i, i]) * (b[i] - tmp1 - tmp2);
                }

                sumx = 0;
                sumx1 = 0;
                for (int i = 0; i < n; i++)
                {
                    sumx += x[i];
                    sumx1 += x1[i];
                }

                ++count;
            } while (Math.Abs(sumx - sumx1) > eps);

            return x1;
        }


Dla przykładowego układu równań:


Korzystając z metody Jacobiego i wymaganej dokładności eps = 0.0000001 wymagane są 24 iteracje. W przypadku metody Gaussa – Seidela tylko 13.


Prosty programik implementujący powyższe metody można pobrać stąd, a kod programu stąd. Należy wziąć pod uwagę aspekt, że nie uwzględniliśmy sytuacji gdy na głównej przekątnej macierzy znajduje się 0. Wtedy wykonanie kodu w takiej postaci jak jest spowoduje błąd podczas dzielenia. W takich sytuacjach można przykładowo przestawić wiersze i/lub kolumny. Ale to może kiedy indziej opiszę.