Jaki ORM zamiast Linq to Sql?

Nadszedł czas refaktoryzacji i zmian technologicznych w projekcie nad którym obecnie pracuję.

Najważniejsza była zmiana narzędzia dostępu do bazy danych poprzez Linq to SQL na prawdziwego ORM’a. Dużo czytałem ostatnio o Linq to Sql i chociaż jest super łatwy do zastosowania to ma kilka wad. Po pierwsze nie jest to pełnoprawne narzędzie ORM a po drugie wiele mówi się o tym, że technologia ta nie będzie już wspierana przez Microsof (w zamian za to powstał nowy Entity Framework) – nie wiem na ile jest w tym prawdy ale wiele się ostatnio na ten temat naczytałem więc musi coś w tym być…

Po długich namysłach postanowiłem wykorzystać Fluent nHibernate który likwiduje konieczność pisania mapowań w plikach XML. Nie znam języka zapytań nHibernate więc postanowiłem obejść ten problem poprzez zastosowanie Linq to nHibernate. W ten sposób możemy pobierać dane poprzez język zapytań Linq a zarazem korzystać z potężnego narzędzia jakim jest nHibernate. I to wszystko bez konieczności konfigurowania mapowań w XML’u. Czegóż chcieć więcej:).

Od razu chciałbym opisać mój pierwszy problem który napotkałem. W relacji jedeen-do-jednego, gdy tworzymy obiekt a wartość id nie jest generowana przez bazę danych tylko przypisana przez nas należy w mapowaniu fluent dodać :

Id(x => x.UserId).Unique().GeneratedBy.Assigned();

Zajęło mi 4 godziny żeby to wpaść;/. No cóż – jeśli chodzi o nHibernate jestem noobem:P.

Podstawy SEO

Podczas pracy w wakacje przeszedłem szkolenie SEO (Search engine optimization). Zrobiłem kilka notatek i chciałbym się nimi z wami podzielić (od razu zaznaczam, że nie jestem specjalistą w tej dziedzinie- zostały mi jedynie przekazane wskazówki dotyczące pisania poprawnego, czyt. zoptymalizowanego pod SEO, kodu html).

Od czego zacząć? Po pierwsze gdy tworzymy serwis powinniśmy się zastanowić jak bardzo powinniśmy się skupiać na SEO dla naszej strony. Istnieją trzy podziały w zależności od ukierunkowania naszego serwisu:

- bardzo małe

seo-very-lowBardzo mała treść jest udostępniona dla botów internetowych. Często jest to jedynie strona główna, strony w stopce, strony wyjaśniające cel działania strony i tyle. Które serwisy korzystają z takiego seo? Głownie takie, które muszą chronić np. dane osobowe zarejestrowanych użytkowników jak np. serwisy społecznościowe, randkowe itd. Takie serwisy zyskują „traffic” raczej poprzez reklamę i linki niż samo szeroko pojęte SEO.

- średnie

seo-middleTutaj już więcej danych jest „wystawionych” dla crawlerów. Dobrym przykładem tutaj są np. fora. Wszystkie dyskusje są „w SEO” ale np. konta użytkowników ich profile są ukryte.

- bardzo duże

seo-highW końcu ostatni rodzaj serwisów pod względem SEO. Tutaj praktycznie wszystko jest indeksowane. Dobrym przykładem są sklepy internetowe- indeksowane jest praktycznie cała treść. Jedynie może konta użytkowników są ukryte ale też nie zawsze. W tym przypadku im więcej zindeksowane tym lepiej.

Ok mamy za sobą mały wstęp i omówiony szybko podział serwisów z względu używanego SEO. Teraz chciałbym krótko opisać kilka waznych technik SEO i na koniec opowiedzieć o idealnym serwisie z punktu widzenia SEO.

A więc konkretnie:

  • Zawsze ustawiać title i unikalne description strony. Dlaczego? Bo strony o tym samym title są uznawane za duplikację treści. Jak to zrobić? Możemy np. wpisać jako title adres naszej strony (www.strona.pl) i dodać aktualną nazwę działu i produkt który przeglądamy (www.strona.pl — sklep — głośniki komputerowe logitech 1231)
  • Przekierowania permanentne 301 zamiast przekierowań tymczasowych 302- robiąc przekierowanie 301 dajemy do zrozumienia botom że obecna strona jest już nieaktualna i należy ją usunąć z indexu
  • Keywords’y powinny ograniczyć się do max 10- lepiej mniej z większą siłą, powinny one skupić się na głównej treści serwisu (np.: zakłady, bukmacherskie, hazard, zakłady sportowe)
  • Linki zawsze powinny mieć tzw. Anchor text który jest za razem słowem kluczowym do linku
  • Ilość linków na stronie głównej powinna wynosić nawet do 400, w podstronach do 100. Dlaczego tak wiele i jak to zrobić? Strona główna powinna być swoistą bramą dla strona które są w głębi naszego serwisu. To właśnie strona główna ma największą moc SEO i trzeba z niej skorzystać. Jak dodać aż 400 linków? W stopce, pogrupować je w ładny „akordeon” itd.
  • Wyniki z wewnętrznych wyszukiwarek strony nie powinny być indexowane. Może to tylko zepsuć seo naszej strony ponieważ niektóre wyniki będą znalezione kilka razy przez co traktowane jako  duplikowane treści. 

  • Hierarchiczna struktura html- h1, p, h2, (keyword, text, wariacje keywords). Tutaj chodzi o to, żeby tekst był poprawnie sformatowany a w jego treści przeplatały się słowa kluczowe naszej strony. Oczywiście dobrze byłoby gdyby była to unikalna treść.

  • Nazwy obrazków powinny odnośić się do treści a tagi img zawsze powinny mieć odpowiedni alt text. Tego chyba nie trzeba komentować.

  • Url routing-> zamiast www.zaloz-sie.pl/zaklad.aspx?id=123123123 lepiej będzie www.zaloz-sie.pl/zaklad/czy-jutro-spadnie-snieg/12312312 – od strony seo jest to gigantyczna różnica ponieważ teraz każda strona z zakładem będzie traktowana jako nowa strona w naszym serwisie (chociaż prowadzi do jednego pliku) . Są to tak zwane „visible url’s”

  • Wybrać jeden typ adresu: albo www.adres.pl albo adres.pl- dla botów to całkowicie inne strony

Jak więc powinien wyglądać idealny serwis od strony SEO? Powyższe wyliczenia powinny już to wyjaśnić. A więc: dobry i wpadający w pamięć adres, do 10  słów kluczowych, na stronie głównej do 400 linków prowadzących w głąb strony, brak błędów w dokumencie, hierarchiczna struktura, unikalne treści zawierające wariacje słów kluczowych, „visible url’s”.

Jakimi narzędziami wspomagać się do SEO? Po pierwsze dwa dodatki:

Na tym kończę mój post. Mam nadzieje, że mój post przyda się i zmusi do przemyśleń. Zapraszam do dyskusji.

  • Zawsze ustawiać title i unikalne description strony

ASP .NET i jQuery- jak renderować dane?

Chciałbym dzisiaj omówić problem renderowania danych pobranych poprzez AJAX’owe zapytanie poprzez jQuery w ASP .NET(chociaż metoda tak na prawdę jest uniwersalna dla PHP, JSP itd.).

Można wszystko wyrenderować „na piechotę”- czyli iterując przez otrzymany wynik w postaci XML lub JSON, sklejać to do string’a a następnie przypisać do pożądanego kontenera i wyświetlić. Takie rozwiązanie oczywiście spełni swoją rolę ale ma kilka poważnych wad:

  • jest sprzeczne z oddzieleniem logiki naszej aplikacji z warstwą prezentacji- powoduje to zamieszanie w kodzie i frustrację
  • wyobraźcie sobie, że grafik daje wam nowy template strony- będziecie mieli przed sobą duuuużo żmudnej i nudnej pracy

Co zrobić w takim przypadku? Ano zastosować silnik do renderowania danych po stronie klienta. Moim ulubionym jest jTemplates i to na nim będzie się opierać mój przykład. Dobra koniec wstępu bierzmy się do pracy:).

Cały projekt jest dostępny do pobrania na końcu wpisu więc nie będę opisywał po kolei jak tworzyć projekt itd. bo moim zdaniem mija się to z celem i wydłuża niepotrzebnie ten wpis.

W naszej solucji mamy następujące pliki:

Screen shot 2009-09-29 at 16.55.39W folderze js mamy bibliotekę jQuery w wersji 1.3.2, jTemplates to wspomniany wyżej silnik renderowania po stronie klienta w wersji 0.7.8 i livequery- plugin który wykrywa zmiany w DOM naszego dokumentu html i jeżeli jest taka potrzeba przypisuje zdarzenia do nowych obiektów które pojawiły się na stronie (troszkę niefortunnie to wytłumaczyłem ale będzie przykład).

FooWrapper  i Game właściwie tylko „opakowują” nasze obiekty i tyle powinniśmy o nich wiedzieć- kilka properties- nic ciekawego:).

Ok mamy za sobą opis projektu więc skupmy się na ciekawych rzeczach. Zawartość pliku Default.aspx:

W sekcji body:

<form id="form1" runat="server">
<div>

<asp:Button ID="btnAjaxCall" runat="server" Text="Click me!" />

<div id="container"></div>

</div>
</form>

Po krótce przycisk pod którego podpięte jest zdarzenie i kontener w którym będzie wyświetlana treść.

Sekcja head jest ciekawsza:

$(document).ready(function() {
 
$("#&lt;%= btnAjaxCall.ClientID %&gt;").click(function(e) {
 
$.ajax({
type: "POST",
cache: true,
async: false,
url: "WebMethods.aspx/GetProduct",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
beforeSend: function() {
 
},
success: function(msg) {
 
$('#container').setTemplate($("#TemplateResultsTable").html());
$('#container').processTemplate(msg);
 
},
error: function(msg) {
 
}
});
 
$("span.spanClass").click(function() {
 
var bar = $(this).attr("title");
 
alert(bar);
});
 
e.preventDefault();
 
});
 
});

Najbardziej tutaj interesuje nas oczywiście zdarzenie podpięte pod przycisk „btnAjaxCall”.  Jest to standardowe AJAX’owe zapytanie do statycznej metody osadzonej w stronie WebMethods.aspx o nazwie GetProduct. Jej kod wygląda następująco:

[WebMethod]
public static FooWrapper[] GetProduct()
{
 
List&lt;FooWrapper&gt; foos = new List&lt;FooWrapper&gt;();
 
FooWrapper foo1 = new FooWrapper();
 
foo1.Name = "John Doe";
foo1.Bar = "Nothing";
foo1.IsOld = false;
List&lt;Game&gt; mariuszGames = new List&lt;Game&gt;();
Game game1= new Game();
game1.Title = "UnrealTournament 2004";
Game game2 = new Game();
game2.Title = "Quake 3";
mariuszGames.Add(game1);
mariuszGames.Add(game2);
foo1.Games = mariuszGames.ToArray();
 
FooWrapper foo2 = new FooWrapper();
foo2.Name = "Stefan Schmidt";
foo2.Bar = "Nix";
foo2.IsOld = true;
List&lt;Game&gt; stefanGames = new List&lt;Game&gt;();
Game game3 = new Game();
game3.Title = "DragonBall";
Game game4 = new Game();
game4.Title = "StreetFighter";
stefanGames.Add(game3);
stefanGames.Add(game4);
foo2.Games = stefanGames.ToArray();
 
foos.Add(foo1);
foos.Add(foo2);
 
return foos.ToArray();
 
}

Czyli ogólnie mówiąc zwraca przykładowe dane w postaci tablicy obiektów FooWrapper która przez jQuery automatycznie zostanie serializowana do postaci JSON. Tak wynik zapytania wygląda w firebug’u:

Screen shot 2009-09-29 at 17.13.30

Teraz jedyne co nam pozostaje to wyrenderować dane. Tak jak pisałem na początku do tego celu użyjemy pluginu jTemplates. Jego wywołanie wygląda następująco (w metodzie success zapytania AJAX):

$('#container').setTemplate($("#TemplateResultsTable").html());
$('#container').processTemplate(msg);

Obiekt „TemplateResultsTable” jest naszym templatem który jest zdefiniowany także w sekcji head strony i wygląda następująco:

<script type="text/html" id="TemplateResultsTable">
{#template MAIN}
{#foreach $T.d as FooWrapper}
{#include ROW root=$T.FooWrapper}
{#/for}
</table>
{#/template MAIN}
{#template ROW}
<span title="{$T.Bar}" {#if $T.IsOld == true}  style=" color: Red;"  {#else} style=" color: Blue;"    {#/if}>{$T.Name}</span>
<p>Games owned</p>
<ul>

{#foreach $T.Games as Object}
<li>{$T.Object.Title}</li>
{#/for}

</ul>
<br />

{#/template ROW}
</script>

Ciekawą rzeczą tutaj jest składnia przypominająca Pythona (która nawiasem mówiąc jest niezwykle wygodna) i zagnieżdżona pętla foreach iterująca po obiektach Games. Warto tutaj zauważyć, że obiekt Games w odpowiedzi JSON nie posiada typu więc należy odnosić się do niego jako Object.

A oto wynik naszej aplikacji:

Na początku tylko button:
Screen shot 2009-09-29 at 18.49.29

Po naciśnięciu wyrenderowane dane:

Screen shot 2009-09-29 at 18.47.19

A tyle trwała cała operacja:

Screen shot 2009-09-29 at 18.49.44

Na koniec małe podsumowanie. W tym przykładzie chciałem wam przedstawić jak łatwo stworzyć szybkie, niezawodne i eleganckie rozwiązanie służące do renderowania po stronie danych, pochodzących z zapytania AJAX, w formacie JSON.

Link do projektu: jTemplates example in ASP .NET

PS jak tylko znajdę dobry plugin do WordPress’a do kolorowania składni nie omieszkam poprawić tego postu.

Skype extension for Firefox- zło powodujące problem cachowania strony

Od update’u do Firefox’a 3.5 na naszej stronie w firefox’ie mieliśmy ciągle problemu z pozostającym cachem i co za tym idzie brakiem odświeżania strony. Np. po zalogowaniu strona nie odświeżała się- dopiero ctrl+F5 pomagał. Dzisiaj ten problem zaczął już mnie poważnie denerwować i postanowiłem coś z nim zrobić. Na początek wyłączyć wszystkie pluginy w firefoxie. Po tym problem zniknął. Następnie włączałem kolejno jeden za drugim by znaleźć winowajcę… I??? Tadaaaa- okazał się nim „Skype extension for Firefox”.

Okazało się, że jest to dość znany problem- link do wątku na oficjalnym forum skype’a. Dużo wielkich serwisów (nasza klasa, finansowo.pl) miało dokładnie ten sam problem. Fix ponoć w drodze a do tego czasu pozostaje nam tylko wyłączyć ten plugin lub całkowicie go odinstalować.

Najgorsze jest w tym to, że KAŻDY użytkownik skype’a i firefox’a ma to „coś” na komputerze zainstalowane. Plugin instaluje się automatycznie przy instalacji skype’a bez możliwości zrezygnowania z niego… Jedyną możliwością pozbycia się go, jest zrobienie to już po instalacji (poprzez odinstalowanie- w programach jest on wymieniony jako „Skype web features”, lub wyłączenie w przeglądarce).

Na koniec dodam, że po wyłączeniu tego plugin’u w końcu poprawnie zaczął funkcjonować w firefox’ie gmail, google reader… Myślałem, że złe działanie tych aplikacji było winą firefox’a ale na szczęście okazało się, że „bękartem” był ktoś innySmile.

btw. Przepraszam Cię firefox’ie za te wszystkie psy(lisy) które na Tobie powiesiłem:).

Jaki AJAX do ożywienia strony w ASP .NET 2.0

Projekt przy którym obecnie pracuję jest tworzony w technologii ASP .NET 2.0 i z racji tego, że stoi na serwerze apache z nakładką mono_mod nie mogę używać w pełni ASP .NET 3.5 i ASP .NET AJAX 3.5. Jakiego więc używać silnika AJAX aby ożywić stronkę i dodać jej animuszu?

Po wielu godzinach udało mi się skonfigurować mono aby używać ASP .NET AJAX 1.0. Jednak po wielu testach wydajnościowych postanowiłem oprzeć całą funkcjonalność o AJAX oferowany przez jQuery. Dlaczego? Może na początek małe porównanie.

 

ASP .NET AJAX

za:

  • prostota użycia- tak naprawdę nie trzeba nic wiedzieć o AJAX’ie
  • brak konieczności pisania w Java Scrip’cie

przeciw:

  • jak to z każdym tego typu frameworkiem bywa- gdy coś się psuje ciężko to zdiagnozować
  • strasznie wolny
  • działanie na zasadzie „partial postback”- mało optymalne rozwiązanie powodujace niepotrzebne zawiłości w kodzie serwerowym
  • mało kontrolek i rozszerzeń- tak na prawdę tylko biblioteka ASP .NET AJAX Control Toolkit- nie mogłem z niej niestety korzystać ponieważ większość kontrolek nie działała na serwerze mono
  • częste błędy i wyjątki jak np. „unnamed class wrapper” które nic nie mówią i psują całkowicie funkcjonalność strony
  • brak pełnego wsparcia mono

 

jQuery

za:

  • lekkie, zwinne i szybkie zapytania AJAX’owe
  • zawsze wszystko pod kontrolą- wystarczy co nieco Java Script’u
  • ponad 4000 pluginów, rozszerzeń- nie znalazłem jeszcze czegoś, co nie byłoby już przez kogoś napisane
  • niesamowita społeczność- zawsze można liczyć na pomoc
  • bardzo łatwe zapytania AJAX’owe post i get
  • format JSON do przekazywania odpowiedzi serwera- minimalizm pozwalający ograniczyć ruch na serwerze
  • całkowita kontrola nad tym co dzieje się na stronie poprzez proste operacje DOM
  • ważne w moim przypadku- bezproblemowe działanie na serwerze mono (AJAX post i get dla wywoływać web service’a wymagało przestawienia solucji na asp .net 3.5- po tym zabiegu wszystko działało bezproblemowo)

przeciw:

  • konieczna znajomość JavaScript’u i składni jQuery
  • wymaga więcej wysiłku(przynajmniej na początku żeby się „przestawić”)
  • wszystkie zapytania do serwera mają postać albo WebService’a albo funkcji statycznych przez co nie mamy dostępu do kontrolek serwerowych naszej strony- oczywista sprawa jednak na początku wymagała wiele wysiłku, żeby się przełamać (miałem te same opory przy ASP .NET MVC)

Prosty przykład z życia wzięty- strona w moim obecnym projekcie, która używała kontrolki updatepanel należącej do ASP .NET AJAX, potrzebowała na częściowe odświeżenie zawartości ok 4-7 sekund. To zdecydowanie za dużo- strona wydawała się ociężała jak ruski czołg wlekący się pod górkę.

Po zastosowaniu jQuery do pobrania danych z WebService’a i wyrenderowaniu danych poprzez plugin jTemplates(uwielbiam go) ta sama czynność zajmuje w najgorszym wypadku troszkę ponad 1 sekundę. To przyśpieszenie ok 5-7 razy. Czy to nie cudowne? Strona już nie wydaje się ociężała ale zwinna niczym myśliwiec F15.

Odpowiedź serwera w pierwszym przypadku zajmowała ok 50kb. W drugim było to niecałe 3kb. Oczywiście gdyby zastosować same wywołanie metody z web service’a poprzez ASP .NET AJAX zamiast stosowania kontrolki update panel i partial postback’u można by zoptymalizować renderowanie strony ale uważam, że użycie jQuery i silnika renderującego po stronie klienta takiego jak jTemplates jest rozwiązaniem niezwykle wydajnym i przede wszystkim wygodnym.

Jako przykład podaję stronę z mojego obecnego projektu: link. Zapytanie AJAXowe trwa ok 1s i dostarcza dane w formacie JSON o wielkości 2KB. Wszystko z eleganckim rozdzieleniem warstwy prezentacji (jTemplates) i logiki biznesowej (pobieranie danych poprzez web service i formatowanie do postaci JSON).

Dodane:

Poprzedni link który umieściłem prowadził do stony z programami uczelnianymi obecnego projektu- nie działał z powodu błędu spowodowanego brakiem wywołania metody zapewniającej anonimowy dostęp do web service’a. Dziękuję za znalezienie tego błędu. Oto poprawiony link.