poniedziałek, 3 marca 2014

3 egzaminy za 1

Microsoft jakiś czas temu udostępnił bardzo fajną promocję: płacąc za 1 egzamin możemy zdawać aż 3. Czasu nie pozostało wiele - promocja kończy się końcem maja 2014.
Jeżeli ktoś jest zainteresowany więcej szczegółów można znaleźć na stronie http://borntolearn.mslearn.net/btl/b/veronica/archive/2013/11/01/t-and-c-for-developer-exam-offer-2013.aspx#fbid=P7LajA7aSYu
Warto skorzystać zwłaszcza w takiej cenie :)

piątek, 28 lutego 2014

Autoryzacja w MVC 5 - ASP.NET Identity

Kiedyś pisałem o MVC 2 później 3, 4 a teraz przyszedł czas na nowości dotyczące 5 wersji frameworka. Wraz z MVC wypuszczono także nową bibliotekę pozwalającą autoryzować użytkowników - Microsoft Identity. Ma ona za zadanie zastąpić przestarzały MembershipProvider i nie końca udany SimpleMembership.
Co tym razem przygotował Microsoft? Możliwości ASP.NET Identity:
  • łatwe dodawanie dodatkowych informacji do profilu użytkownika
  • domyślnie użycie Entity Framework jako ORM
  • kontrola zapisu (możliwość wyboru czy dane mają zostać zapisane w SQL Azure, MS SQL, NOSQL itp)
  • wsparcie dla TDD dzięki dużo lepszej architekturze frameworka
  • wsparcie dla ról w aplikacji
  • system nadawania praw do poszczególnych aspektów aplikacji
  • zewnętrzna autoryzacja (Facebook, Twitter itp)
Jak widać całkiem sporo możliwości. Szczególnie ostania opcja jest przydatna w obecnych czasach. Dodatkowo po stworzeniu projektu wszystko co jest potrzebne jest już dla nas stworzone i jedyne co musimy zrobić aby korzystać z np. Twittera jako sposobu autoryzacji to podanie danych do konta naszej aplikacji.
Schemat bazy ASP.NET Identity przedstawia się następująco:


Dużo przyjemniejszy niż w przypadku starego MembershipProvidera. Dodawanie nowych kolumn i informacji o użytkowniku też nie sprawia problemów:
Wystarczy zlokalizować klasę IdentityUser i dodać w niej nowe pole:


Code:
using System;
using Microsoft.AspNet.Identity.EntityFramework;

namespace AuthorizationSample.Models
{
    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
        public DateTime? BirthDate { get; set; }
        public string City { get; set; }
    }

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

Jeżeli korzystamy z mechanizmu automatycznych migracji, wystarczy że utworzymy migrację i uaktualnimy bazę danych.

Projekt domyślnie tworzy szablonowe widoki tworzenia i modyfikowania danych użytkownika. Dostajemy gotowe klasy modeli. Jedynie co brakuje to metod odpowiedzialnych za widoki. Jeżeli chcemy użyć innej klasy (np. już istniejącej w systemie) do przechowywania informacji o użytkowniku, wystarczy że zaimplementujemy interfejs IUser:

Code:
  public interface IUser
  {
    /// <summary>
    /// Unique key for the user
    /// </summary>
    /// 
    /// <returns>
    /// The unique key for the user
    /// </returns>
    string Id { get; }

    string UserName { get; set; }
  }

Jak widać Id użytkownika przetrzymywane jest tutaj jako pole typu string - i takiego też typu jest domyślnie tworzone w bazie danych. Czy to dobre rozwiązanie? Moim zdaniem nie do końca. Dużo lepszym rozwiązaniem byłoby pozwolenie deweloperowi wybrania czy Id ma być łańcuchem, Guidem czy też liczbą. W nowej wersji frameworka (beta) poprawiono tę niedogodność.

Kod bibliotek .NET Online

Microsoft udostępnił nową wersję portalu umożliwiającego przeglądanie kodu bibliotek .NET.
Dostęp do narzędzia dostępny jest pod adresemhttp://referencesource-beta.microsoft.com/



Narzędzie jest proste w użyciu, nawigacja nie sprawia problemów. Wygląd, kolorowanie i podział na "Solution Explorer" dodatkowo uprzyjemnia przeglądanie.
W przypadku braku dostępu do narzędzi typu Reflector czy ILSpy świetnie się sprawdzi w celu szybkiego prześledzenia kodu w bibliotece .NET.

sobota, 12 października 2013

Czy .NET ma przyszłość?

Z pewnością wielu zadaje sobie to pytanie podchodząc do nauki nowej platformy.
Języka programowania można nauczyć się bardzo szybko - znając jeden opanowanie kolejnego zajmie maksymalnie dzień.
Nauka platformy i frameworków może zająć miesiące a nawet dłużej. Frameworki ewoluują, dostarczają nowe lepsze rozwiązania często pojawiających się problemów. Nauka i zmiana przyzwyczajeń związana jest z każdą nową wersją.
Naturalnie pojawia się więc pytanie czy warto jest inwestować swój czas w daną platformę, tutaj .NET. Świetnie na to pytanie odpowiada Rockford Lhotka. Zachęcam do przeczytania jego postu na ten temat - http://www.lhotka.net/weblog/DoesNETHaveAFuture.aspx.

poniedziałek, 30 września 2013

nHibernate logowanie operacji

Bardzo często chcemy wiedzieć co dzieje się w naszej aplikacji. Podstawową formą śledzenia aplikacji jest logowanie informacji do pliku/bazy lub innego nośnika z którego można następnie je odtworzyć i analizować.

nHibernate świetnie w tej materii współpracuje z biblioteką log4net. Bibliotece tej poświęcę osobny post - w skrócie umożliwia ona tworzenie logów. Konfiguracja lo4net możne zostać zapisana w pliku konfiguracyjnym aplikacji jak i bezpośrednio w kodzie.

Aby logować to co dzieje się w nHibernate należy dodać do solucji biblioteki. Można tego dokonać z poziomu NuGet:


Dzięki użyciu NuGet log4net zostanie wstępnie skonfigurowany w app.config. Po uruchomieniu aplikacji korzystającej z nHibernate, na ekranie konsoli zostaną wypisane informacje z aktualnie wykonywanych czynności w nHibernate:


Dodatkowo wchodząc w ustawienia session factory możemy dodać parametr show_sql. Parametr ten doda w trybie logowania na konsolę dane wartości przekazywanych parametrów do zapytania SQL:

Code:
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
    <session-factory>
      <property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.connection_string">Server=localhost;initial catalog=AdventureWorksLT2012;Integrated Security=True</property>
      <property name="show_sql">true</property>
      <mapping assembly="AdventureWorksSampleHibernateLogging" />
    </session-factory


W tym poście przedstawiłem w jaki sposób logować na konsolę. Nic nie stoi na przeszkodzie aby te dane logowane były do bazy czy pliku. Wybór należy do nas.

Plik z solucją: http://sdrv.ms/17kyth8

niedziela, 29 września 2013

Generowanie kluczy głównych w nHibernate

Każda tabela w bazie danych powinna posiadać kolumnę zwaną kluczem głównym. Dzięki tej kolumnie jesteśmy w stanie jednoznacznie odróżnić wiersze w bazie danych.
nHibernate posiada kilka generatorów kluczy głównych. Możemy je podzielić na dwie kategorie:
  • takie, które wymagają odpytania bazy danych po wstawieniu rekordu do bazy o wygenerowany id
  • takie, które wstawiają id wraz z wierszem
Pierwszy sposób jest niekorzystny, gdyż aby pobrać wartość id klucza należy wywołać dodatkowe zapytanie do bazy danych - zwiększamy obciążenie na bazie danych, jednocześnie zmniejszając wydajność aplikacji.

Pierwsza grupa zawiera następujące generatory:
  • identity -zwraca id wygenerowane przez bazę
  • select - używa zapytania SELECT aby pobrać id rekordu (potrzebny jest w takim wypadku klucz naturalny)
  • sequence-identity - używany dla baz obsługujących sekwencje
  • trigger-identity - zwraca id wygenerowane przez trigger
  • native - w zależności od bazy danych (np. dla MS SQL identity, dla Oracle - sequence-identity)
Druga grupa jest szersza i zawiera więcej możliwości:
  • hilo - użyty jest algorytm Hi/Lo - rezerwowany jest zakres identyfikatorów numerycznych. Po skończeniu danego zakresu rezerwowany jest kolejny. Jest to bezpieczne rozwiązanie, gdyż użyta jest w bazie dodatkowa tabela przechowująca rezerwowane zakresy
  • guid - wykorzystuje GUID
  • guid.comb - łączy 10 bajtowy GUID wraz z 6 bajtową datą. Zmniejsza to poziom fragmentacji indeksu
  • guid.native - pobiera GUID z bazy (wymaga to odwołania do bazy dnaych)
  • uuid.hex - tworzy GUID numeryczny
  • uuid.string - zapisuje GUID w postaci binarnej - w takiej postaci jest nieczytelny dla człowieka 
  • counter - licznik inicjalizowany z zegara systemowego; nie można go stosować w rozwiązaniach gdzie baza jest współdzielona 
  • increment - rozwiązanie podobne do poprzedniego - wartość początkowa licznika pobierana jest na starcie jako MAX(Id) i inkrementowana w czasie działania programu. Nie nadaje się w rozwiązaniach gdzie jest współdzielona baza danych.
  • sequence - korzysta z sekwencji dostępnych w takich bazach jak Oracle i PostgreSql
  • seqhilo - łączy algorytm Hi/Lo z sekwencjami w celu poprawienia wydajności
  • foreign - używa klucza z innej encji - najczęściej wykorzystywany w przypadku relacji one-to-one
Każdy sposób ma swoje wady i zalety. Najbezpieczniejszą metodą jest GUID, który ze względu na swoją długość zapewnia unikalność pomiędzy wieloma instancjami aplikacji korzystającymi z tej samej bazy danych.
Jeżeli budujemy prostą aplikację, a z bazy będzie korzystała tylko jedna aplikacja możemy spokojnie jako klucza głównego użyć typu całkowitoliczbowego (integer).

nHibernate " is not mapped"

Wyjątek z tematu "[Class] is not mapped" jest jednym z częściej występujących błędów podczas pracy z nHibernate. Związane jest to głównie z tym, że większość pracy wykonujemy manualnie wprowadzając nazwy klas i mapowań jako zwykłe ciągi znaków (string) co przekłada się na ilość błędów.

Przyczyn tego wyjątku można szukać w kilku miejscach. Miejsca w których można popełnić błędy podzieliłem na 4 obszary:
  1. App/web config
  2. Plik mapowania klasy na tabelę w bazie
  3. Tworzenie sesji
  4. Zapytania


1. App/web .config
W pliku tym ustawiamy podstawowe właściwości fabryki sesji. Jednym z ważniejszych parametrów jest ten, mówiący gdzie znajdują się mapowania klas do tabel:


Code:
  <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
    <session-factory>
      <property name="dialect">NHibernate.Dialect.MsSql2012Dialect</property>
      <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
      <property name="connection.connection_string">Server=localhost;initial catalog=AdventureWorksLT2012;Integrated Security=True</property>
      <mapping assembly="nHibernateCreatingMapping" />
    </session-factory>
  </hibernate-configuration>

Po wystąpieniu tego błędu sprawdzamy więc w/w plik i szukamy czy znajduje się tam linijka mówiąca w którym assembly znajdują się mapowania.


2. Plik mapowania klasy na tabelę w bazie
W tym pliku można popełnić szereg błędów:


Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="nHibernateCreatingMapping" namespace="nHibernateCreatingMapping.Domain">
  <class name="ProductDescription" table="SalesLT.ProductDescription">

Rzeczy które sprawdzamy:
  • czy nazwa assembly jest na pewno poprawna
  • czy namespace się zgadza
  • czy w nazwie klasy nie popełniliśmy błędu
  • czy we właściwościach pliku zaznaczyliśmy Build Action na Embedded Resource
Jak widać możliwość popełnienia 4 błędów - całkiem sporo.


3. Tworzenie sesji:


Code:
            ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
            var session = sessionFactory.OpenSession();

Tutaj także można popełnić niewinny błąd i przez pośpiech możemy zapomnieć o wywołaniu metody Configure przed stworzeniem obiektu fabryki sesji.


4. Zapytania
Jeżeli do tworzenia zapytań wykorzystujemy HQL, należy pamiętać że w zapytaniach używamy nazwy klasy a nie tak jak by się mogło wydawać - tabeli.


Code:
var productDescriptions = session.CreateQuery("from ProductDescription").List<ProductDescription>();



Wiele osób porzuca nHibernate z powodu problemów konfiguracyjnych. Po kilku dniach używania biblioteki można w łatwy sposób identyfikować miejsca gdzie potencjalnie popełniliśmy błąd. Aby ułatwić sobie pracę i przy okazji używać silnie typowanych danych można skorzystać z fluent nHibernate - ale o tym w osobnym poście.