niedziela, 23 września 2012

Mapowanie obiektów - Extension Methods

Extension Methods można wykorzystać nie tylko do dodawania metod do istniejących klas, ale także jako najprostszy mechanizm mapujący. Nie musimy tworzyć całej biblioteki do mapowania, wystarczy dodać do klasy odpowiednią metodę np. Map.

Jako kod bazowy użyjemy obiekt encji tabeli:

Code:
    public class PersonEntity
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int Age { get; set; }
        public string Address { get; set; }
    }

klasa którą chcemy uzyskać ma następującą strukturę:

Code:
    public class PersonView
    {
        public string FullName { get; set; }
        public string Address { get; set; }
    }

Kod mapowania:

Code:
    public static class Mappers
    {
             public static PersonView Map(this PersonEntity personEntity)
             {
                 return new PersonView
                            {
                                FullName = personEntity.FirstName + " " + personEntity.LastName,
                                Address = personEntity.Address
                            };
             }
    }

użycie sprowadza się do wywołania metody Map na obiekcie bazowym:

Code:
            var personEntity = new PersonEntity
                                   {
                                       Id = 1, Address = "Andrychowska 34, 10-124 Andrychów", Age = 31, FirstName = "Jan", LastName = "Kowalski"
                                   };
            var pv = personEntity.Map();

wtorek, 18 września 2012

TortoiseSVN Ignore dla C# i Resharper

Przydatny filtr dla ignorowanych w przypadku korzystania z Resharpera:

*\bin* *\obj* *.suo *.user *.bak **.ReSharper** **\_ReSharper.** StyleCop.Cache

Dzięki temu unikniemy komitowania folderów bin, obj i wszystkich zbędnych rzeczy - łącznie z plikami generowanymi przez Resharper.
Filtr znalazłem na blogu: Struggles by Lars C.

niedziela, 16 września 2012

jQuery selekcja

Przeglądanie drzewa DOM dokumentu HTML jest bardzo proste przy użyciu selektorów z jQuery.

$("h1") - selekcja wszystkich elementów h1
$(".klasa") - selekcja wszystkich elementów posiadających klasę klasa
$("#id_elementu") - selekcja elementu o id = id_elementu - jest to najszybszy dostępny selektor
$("*") - pobiera wszystkie elementy na stronie
$("p.klasa") - wybiera wszystkie elementy p posiadające klasę klasa
$("p:first") - wybiera pierwszy element typu p
$("[name]") - wybiera wszystkie elementy posiadające atrybut name
$("form[name='formatka1']") - wybiera wszystkie formularze o nazwie formatka1
$("a, p, div") - wyszukiwanie wielu na raz
$("div p") - wybiera wszystkie paragrafy znajdujące się w elemencie div

Metody pomocnicze:
$(".klasa").parent() - wyszukuje wszystkie elementy nadrzędne do tych o klasie klasa
$(".klasa").children() - Elementy zawarte w elementach o klasie klasa
$(".klasa").prev() / .next() - element po prawej/lewej stronie elementów o klasie klasa

Korzystając z wymienionych funkcji można w łatwy sposób zmieniać całą strukturę drzewa DOM dokumentu HTML.

Dodatkowo warto pamiętać aby każdy blok kodu jQuery rozpoczynać od sprawdzenia czy dokument został załadowany:

Code:
$(document).ready(function() {
});

sobota, 15 września 2012

Czym się różni Single od First w LINQ

W LINQ mamy dwie metody First() oraz Single().
Obie metody zwracają po jednym rekordzie. Jaka jest więc między nimi różnica?

Zobaczmy na kawałek kodu i jego reprezentację w bazie MS SQL (zapytania wyciągnięte z Profilera):


Code:
        static void Main(string[] args)
        {
            var test1Context = new test1Context();
            var single = test1Context.Pojazds.Single(x => x.Id == 100);
            var first = test1Context.Pojazds.First(x => x.Id == 100);
        }

SELECT TOP (2) [Extent1].[Id] AS [Id], [Extent1].[Nazwa] AS [Nazwa]FROM [dbo].[Pojazd] AS [Extent1]WHERE 100 = [Extent1].[Id]
SELECT TOP (1) [Extent1].[Id] AS [Id], [Extent1].[Nazwa] AS [Nazwa]FROM [dbo].[Pojazd] AS [Extent1]WHERE 100 = [Extent1].[Id]


Jak widać Single wybiera 2 rekordy a First tylko jeden. Dlaczego?
W przypadku Single pobierane są dwa pierwsze rekordy, które zwróci zapytanie. Następnie sprawdzane jest czy faktycznie jest jeden rekord, a w przypadku gdy są dwa rzucany jest wyjątek. W przypadku metody First wyjątek nie zostanie rzucony, jeżeli zapytanie zwróci więcej niż jeden wynik.

Ma to swoje zalety, w przypadku gdy musimy otrzymać tylko jeden wynik a z jakichś względów otrzymujemy więcej wyników. Korzystając z metody Single możemy szybciej zdiagnozować problemy, które mogą się objawić w aplikacji.

Wstawianie wielu rekordów za pomocą LINQ - Batch Instert by LINQ

Wielu sobie zadaje pytanie dlaczego wstawianie dużej ilości rekordów do bazy za pomocą LINQ2SQL bądź też Entity Framework trwa tak długo.

Zobaczmy na poniższy kawałek kodu:


Code:
        static void Main(string[] args)
        {
            var test1Context = new test1Context();
            test1Context.Configuration.AutoDetectChangesEnabled = false;
            test1Context.Configuration.ProxyCreationEnabled = false;
            for (int i = 0; i < 1000; i++)
            {
                var pojazd = new Pojazd {Nazwa = Guid.NewGuid().ToString()};
                test1Context.Pojazds.Add(pojazd);
            }

            test1Context.SaveChanges();
        }

Wydawałoby się że zostanie wykonana pojedyncza transakcja, która wstawi wszystkie rekordy. Przyglądając się profilerowi MS SQL możemy stwierdzić, że tak się nie dzieje:

EXEC sp_executesql N'insert [dbo].[Pojazd]([Nazwa])
values (@0)
select [Id]
from [dbo].[Pojazd]
where @@ROWCOUNT > 0 and [Id] = scope_identity()'
,N'@0 nchar(100)',@0=N'6dd1fb44-61d3-4fce-8ef8-4f0d61492df2       
'


Każdy rekord jest traktowany osobno, a dodatkowo pobierane jest jego Id. Mamy więc do czynienia z sekwencją: wstaw rekord, pobierz Id, przypisz Id do obiektu.

W jaki sposób poradzić sobie z tym problemem? Na ten moment nie ma jednego uniwersalnego rozwiązania zawartego w EF czy też LINQ2SQL. Niestety musimy sami zaimplementować takie rozwiązanie, korzystając z klas do szybkiego wstawiania rekordów do tabel oferowanych przez poszczególnych providerów komponentów do obsługi baz danych. Innym rozwiązaniem jest napisanie własnego "czystego" SQLa który wykonuje INSERT do bazy przy użyciu transakcji. 

Optymalizacja operacji JOIN

Zobaczmy na prosty przykład:

Naszym zadaniem jest przedstawienie ile kół posiada każdy z pojazdów.
Standardowo napiszemy następujące zapytanie:


SELECT p.Nazwa, COUNT(1) AS 'Ilosc kól'
FROM Pojazd p JOIN Kolo k ON p.Id = k.IdPojazd
GROUP BY p.Nazwa 


Zapytanie jest poprawne, jednak ma ono jedną wadę. W przypadku kiedy w tabelach będzie przybywało danych zapytanie będzie stawało się coraz wolniejsze.
Analizując zapytanie możemy dojść do wniosku że główne obciążenie stanowi połączenie dwóch tabel.
Zapytanie to można podzielić na dwa zliczając najpierw w drugiej tabeli koła dla samochodów a następnie łącząc z pierwszą tabelą:

Jeżeli w pierwszej tabeli jest 2 mln rekordów a w drugiej np 10 mln połączenie spowoduje grupowanie 10 mln rekordów. Łatwiej najpierw pogrupować 2 tabelę i połączyć ją z pojazdami niż na odwrót:

SELECT p.Nazwa, k.IloscFROM Pojazd p JOIN (SELECT IdPojazd, COUNT(1) 'Ilosc' FROM Kolo GROUP BY IdPojazd) AS k ON p.Id = k.IdPojazd

Takie zapytanie wykona się w dużo krótszym czasie oraz zużyje znacznie mniej zasobów sprzętowych.

Przetwarzanie zapytania SQL

Zapytanie SQL jest przetwarzane w pewnej ustalonej kolejności. W większości baz danych kolejność ta jest stała i przedstawia się następująco:

(8) SELECT (9) TOP
(1) FROM
(3) JOIN
(2) ON
(4) WHERE
(5) GROUP BY
(6) WITH
(7) HAVING
(1) ORDER BY


Znajomość tej kolejności przydaje się zwłaszcza w przypadku optymalizacji zapytań. Wiedząc że najwcześniej mamy do czynienia z łączeniem tabel, możemy podzapytaniami ograniczyć łączone zbiory co przełoży się na wydajność zapytania.