Framework WIF dostarcza ujednolicony model zabezpieczeń. W łatwy sposób można przełączać się między tokenami generowanymi na podstawie np. loginu\hasła do CardSpace – kod pozostaje niezmienny. Zainteresowanych zapraszam mojego ostatniego artykułu:
Monthly Archives: June 2010
ASP .NET MVC – widoki częściowe, MasterPages
Widoki częściowe pozwalają na wyodrębnienie ze strony pewnych części do osobnego pliku. Załóżmy, że projektujemy widok strony głównej sklepu internetowego. Z pewnością taki widok zawiera m.in. informacje o stanie koszyka, menu, listę kategorii i produkty w promocji. Można oczywiście wszystkie dane umieścić w jednym pliku aspx. Niestety w takim przypadku plik źródłowy widoku byłby bardzo długi i skomplikowany. Lepszym rozwiązaniem jest zapisanie poszczególnych fragmentów do różnych plików – widoków częściowych.
Widoki częściowe zapisane są w pliku o rozszerzeniu ascx. Sposób definiowania widoku częściowego praktycznie niczym nie różni się od standardowej strony ASP .NET MVC. Jedyną różnicą jest wykorzystanie deklaracji Control zamiast Page:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> Ostatnio dodane produkty. To jest widok częściowy.
Widok można podpiąć za pomocą RenderParial:
<%Html.RenderPartial("NazwaWidokuCzesciowego");%>
Warto zauważyć, że widok częściowy może również zostać podpięty do innego widoku częściowego. Tworząc formularz wyświetlający profil użytkownika wszelkie dane kontaktowe i adresowe warto umieszczać w osobnych plikach (np. Contact.ascx, Address.ascx). Oprócz czytelności zyskujemy możliwość wykorzystania tych samych widoków na różnych stronach.
Z kolei MasterPages definiują powtarzalny fragment widoku. Bardzo możliwe, że w naszej aplikacji na każdej podstronie, niezależnie od prezentowanej treści, wyświetla się menu i logo. Nie warto w takim przypadku w każdym widoku pisać kod html odpowiedzialny za wyświetlenie loga czy menu. Lepiej zdefiniować szablon w pojedynczym pliku. Do tego właśnie służą MasterPage. Przykładowy szablon:
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title> <link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> </head> <body> <img src='/logo.jpg' alt='logo'/> Jakaś treść wspólna dla wszystkich podstron. <asp:ContentPlaceHolder ID="MainContent" runat="server" /> </body> </html>
MasterPage w odróżnieniu od zwykłych stron zawiera deklarację Master. W celu określenia miejsc w których będzie pojawiała się treść zależna od konkretnej strony używamy ContentPlaceHolder. W powyższym przykładzie zdefiniowano dwa PlaceHoldery – jeden odpowiedzialny za wstrzyknięcie tytułu strony a drugi za wyświetlanie konkretnej treści. Strona wykorzystująca zdefiniowany szablon powinna wyglądać następująco:
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> <asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server"> Tytul konkretnej podstrony </asp:Content> <asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server"> Treść konkretnej podstrony </asp:Content>
Koncepcja MasterPage nie jest nowa i dla programistów ASP .NET WebForms z pewnością jest już znana.
T-SQL, klauzula OUTPUT
OUTPUT pozwala na zwrócenie wierszy d0danych, zaktualizowanych lub usuniętych za pomocą komend INSERT, UPDATE i DELETE. Zwrócony wynik możemy wyświetlić po prostu na ekranie lub wstawić do innej tabeli. Wyobraźmy sobie, że mamy tabele Employee oraz EmployeeLog zawierającą logi. W celu wykonania loga możemy:
INSERT INTO Employee (ID_EMPLOYEE,FirstName,LastName) OUTPUT getdate(),'wstawiono wiersz',inserted.ID_EMPLOYEE INTO EmployeeLog values(1,'Jakies imie,','Jakies nazwisko');
Równoważny efekt można osiągnąć wykorzystując wyzwalacz. Nawet podobnie do dyspozycji mamy tabele inserted oraz deleted.
W sposób analogiczny realizujemy logi dla polecenia UPDATE:
UPDATE Employee SET FirstName='nowe imie' OUTPUT getdate(),'aktualizacja wiersza',inserted.ID_EMPLOYEE,inserted.FirstName,deleted.FirstName INTO EmployeeLog WHERE ID_EMPLOYEE=1
W powyższym przykładzie inserted.FirstName zawiera imię po aktualizacji a deleted.LastName przechowuje starą wartość (przed aktualizacją). Ostatni przypadek to polecenie DELETE:
DELETE FROM Employee (ID_EMPLOYEE,FirstName,LastName) OUTPUT getdate(),'usunieto wiersz',inserted.ID_EMPLOYEE INTO EmployeeLog where ID_EMPLOYEE=1
Operator Apply (T-SQL)
W SQL Server 2005 wprowadzono operator Apply. Służy on do łączenia tabeli z funkcją zwracającą również tabelę (table-valued function). Po lewej stronie operatora mamy więc zwykłą tabelę (np. "Employees”) a po prawej jakąś funkcję (np. “GetContactInfo”). Apply wywoła GetContactInfo dla każdego wiersza z Employee. W T-SQL występują dwie odmiany operatora: CROSS APLY i OUTER APPLY. Pierwszy z nich złączy wyłącznie te wiersze dla których funkcja zwraca jakiś wynik. Z kolei OUTER APPLY złączy wiersze zastępując brakujące pola wartością NULL. Przykład:
SELECT tbl.FirstName, tbl.LastName, func.Email, func.Phone FROM Employess as tbl CROSS APPLY GetContactInfo(tbl.ID_EMPLOYEE) as func;
Jak już wspomniałem, GetContactInfo wywoływany jest dla każdego wiersza zwróconego przez pierwsze zapytanie SELECT.
Portal w ASP .NET MVC – obietnicepolitykow.pl
Zapraszam wszystkich na mój portal internetowy http://obietnicepolitykow.pl. Stronę napisałem w ASP .NET MVC. Warstwę dostępu do danych zrealizowałem za pomocą EntityFramework. Wykorzystany silnik bazy danych to oczywiście SQL Server 2008:). Na portalu znajdziecie obietnice i cytaty polityków (również radnych z Waszych miejscowości). Treść jest tworzona przez użytkowników a więc każdy z nas może dodać obietnicę, cytat, polityka, partie lub zdjęcie.
Portal wymaga wciąż wiele pracy więc jeśli ktoś z Was ma uwagi to proszę o pisanie e-mail’i na adres umieszczony w dziale kontakt.
ASP .NET MVC, formularze
Formularz w ASP .NET MVC można zdefiniować za pomocą czystego HTML np:
<form action="Clients/Create" method="post"> <input type="text" /> <input type="submit" /> </form>
Znacznie wygodniejszym rozwiązaniem jest jednak użycie metod dostarczonych przez helpery. W ASP .NET MVC podstawowym helperem jest HTMLHelper do którego można uzyskać referencję w widoku za pomocą właściwości HTML. Stworzenie formularza wygląda więc następująco:
<% using (Html.BeginForm()) { %> <%}%>
Domyślnie zostanie użyta akcja w której się znajdujemy – np. /Clients/Create. Można również zdefiniować konkretny kontroler, akcję i tzw. route values:
<% using (Html.BeginForm("akcja","kontroler",new{id=3},FormMethod.Get,null)) { %> <%}%>
Powyższy formularz typu get wywoła w zależności od ustawień routingu “/kontroler/akcja/3” lub “/kontroler/akcja/?id=3 “.
W analogiczny sposób za pomocą helpera można definiować poszczególne pola:
<%=Html.TextBox("FirstName",Model.Client.FirstName) %> <%=Html.TextAreaFor(Model.Product.Description) %>
Ponadto pola mogą być weryfikowane pod kątem zawartości. Najpierw zobaczmy jak w widoku można oznaczać takie pola:
<% using (Html.BeginForm()) { %> <%=Html.ValidationSummary() %> <%=Html.TextBox("FirstName",Model.Client.FirstName) %> <%=Html.ValidationMessage("FirstName", "*")%> <%}>
ValidationSummary pokazuje szczegółowy opis walidacyjny z wszystkich pól. Z kolei ValidationMessage odnosi się wyłącznie do pojedynczego pola. W tym przypadku gdy imię nie przejdzie walidacji pozytywnie zostanie wyświetlona gwiazdka. W kontrolerze można walidować dane i wszelkie błędy zgłaszać za pomocą AddModelError:
public ActionResult Create() { ... this.ModelState.AddModelError("FirstName", "Nieprawdiłowe imię"); ... }
W następnym poście napiszę o MasterPages widokach częściowych – zapraszam.
ASP .NET MVC – definiowanie widoku, podstawy
Widok czyli graficzny interfejs użytkownika definiuje się w ASP .NET MVC za pomocą kilku helperów oraz czystego HTML\XHTML. Prosty widok prawie niczym nie różni się od dokumentu XHTML:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>ViewPage1</title> </head> <body> <div> </div> </body> </html>
Jedyną nowością jest dodanie deklaracji Page oraz atrybutu runat=”server”.
Jak jednak pamiętamy z poprzedniego postu, kontroler do widoku przekazuje tzw. model:
public ActionResult Index() { Model model=new Model(); model.FirstName="Piotr"; model.LastName="Nazwisko" return View(model); }
Model to dowolna klasa zdefiniowana przez użytkownika np:
public class Model { public string FirstName { get; set; } public string LastName { get; set; } }
W jaki sposób dostać się do modelu w widoku? Przede wszystkim należy stworzyć tzw.strong type view (widok z silną typizacją). Najłatwiej zrealizować to za pomocą kreatora (Add->View):
W kreatorze zaznaczamy opcję “Create a strongly-typed view” oraz wybieramy klasę Model. Dzięki temu zostanie wygenerowany następujący kod:
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MvcApplication1.Models.Model>" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>SampleView</title> </head> <body> <div> </div> </body> </html>
Jak widać, kod w atrybucie Inherits ma przypisany typ generyczny System.Web.Mvc.ViewPage<MvcApplication1.Models.Model>. W tej chwili można już za pomocą odpowiednich dyrektyw dostać się do zawartości modelu:
<div> Your first name is: <%=Model.FirstName %> Your last name is: <%=Model.LastName %> </div>
Dyrektywa <%=%> wyświetla po prostu zawartość zmiennej. W identyczny sposób można zwracać wartości funkcji.
Inną dyrektywą jest <% jakiś kod %>. Wykonuje ona dowolny kod:
<div> <%string firstName=Model.FirstName; if (firstName.StartsWith("P")) { %> Your first name is: <%=Model.FirstName%> Your last name is: <%=Model.LastName%> <%} %> </div>
W najnowszej wersji frameworka dodano jeszcze jedną dyrektywę: <%: %>. Służy ona do automatycznego kodowania tekstu (ochrona przed XSS). Przykładowo <%:Model.FirstName%> jest równoważne wywołaniu <%=Html.Encode(Model.FirstName)%>.
Oprócz wspomnianych strong-typed view, można definiować widoki bez podania typu modelu. W takim przypadku deklaracja Page wygląda następująco (brak typu generycznego ViewPage):
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %>
Z kolei wyświetlanie zawartości:
<%= Html.Encode(ViewData["FirstName"]) %>
W następnym poście pokaże jak definiować własne formularze i walidować dane.
Gra w XNA
Jaki czas temu współtworzyłem z przyjaciółmi pewną grę. Byłem odpowiedzialny za m.in. edytor map, efekty, silnik, animacje, AI (wyszukiwanie drogi, zachowanie NPC itp.). Oprócz tego w teamie byli również ludzie zajmujący się programowaniem interfejsu, sieci, testowaniem, dokumentacją oraz tworzeniem grafiki.
Całość projektu składa się z silnika, gry, edytora map oraz kilku narzędzi wewnętrznych. Gra z pewnością nie robi graficznie szału jednak ze względu na fakt oparcia jej na własnym silniku było sporo zabawy (np. z partycjonowaniem wierzchołków). W skrócie silnik umożliwia m.in.:
– kolizje (na trójkątach oraz BB),
– drzewa ósemkowe (na podstawie wierzchołków oraz obiektów),
– efekty bloom, blur,
– normal mapping,
– animacja szkieletowa,
– efekty krwi, headshot,
– wyświetlanie śladów pocisków na ścianach,
– obsługa sieci (możliwy typ gry multiplayer),
– zmieniająca się muzyka w zależności od otoczenia i akcji,
– efekty cząsteczkowe, pogodowe.
W pewnym momencie AI oparliśmy na sztucznych sieciach neuronowych. Zachowania NPC były więc zależne od SSN oraz przygotowanych danych uczących. Niestety po testach stwierdziliśmy, że przygotowanie próbek uczących jest zbyt czasochłonne aby osiągnąć zamierzone efekty. W nowszych wersjach gry zdecydowaliśmy się na tradycyjny klasyfikator oparty na logice rozmytej.
Gra jest mieszanką RTS z shooterem. Kierujemy postacią tak jak w tradycyjnym TPS (za pleców). Możemy zbierać apteczki, bronie oraz samodzielnie walczyć z przeciwnikiem. Ponadto można zbudować własną bazę, wieżyczki, sklepy, silosy itp. Pieniądze zarabiamy niszcząc przeciwnika lub poprzez specjalne budynki (magazyny, gdzie można również przechowywać swój ekwipunek). Takim sposobem po zebraniu odpowiednich funduszy możemy sterować rakieta balistyczną lub czołgiem:)
Screeny (na których widać m.in. naszą uczelnie ;)):
Filmik (na którym widać kilka błędów np. związanych z plikiem językowym;) ):
Model-View-Controller – ASP .NET MVC, wprowadzenie
ASP .NET MVC jest frameworkiem wspierającym wzorzec projektowy MVC. Należy zdawać sobie sprawę, że przechodząc z klasycznego ASP .NET na ASP .NET MVC tracimy możliwość korzystania ze zbioru kontrolek typu ListView, Repeater itp. Na początku może wydawać się to bardzo ograniczające jednak w praktyce czas wymagany na stworzenie aplikacji webowej jest niewiele dłuższy a za to zyskujemy znacznie większą elastyczność. Ponadto ze względu na brak standardowych kontrolek nie ma bałaganu z ViewState. Uogólniając, interfejs tworzymy w ASP .NET MVC za pomocą czystego HTML oraz kilku helperów.
Stwórzmy więc pierwszy projekt. Po wybraniu stosownego szablonu (ASP .NET MVC 2 Web Application) zostaniemy zapytani czy chcemy wygenerować również projekt dla testów jednostkowych:
Po akceptacji w Solution ujrzymy dwa projekty: aplikacja web oraz testy jednostkowe. Przyjrzyjmy się bliżej automatycznie wygenerowanej stronie www.
W projekcie zwartych jest kilka folderów: Content, Controllers, Models, Scripts, Views. W Content zwykle przechowujemy style css, w Scripts skrypty JS a w Models modele (np. wrappery warstwy biznesowej). Views zawiera widoki danych czyli interfejsy webowe napisane np. w XHTML.
Jak widać na screenie, w Views znajdują się podfoldery. Taka struktura nie jest przypadkowa i należy jej przestrzegać. Wiąże się to ściśle z kontrolerami dlatego zobaczmy również jak wygląda ich struktura:
Nazwy klas kontrolerów powinny wyglądać następująco:[Nazwa]Controller. Związane jest to z domyślnym poszukiwaniem widoków dla danego kontrolera. AccountController szuka swojego widoku w folderze Views\Account z kolei HomeController w folderze Views/Home. W Views istnieje jeszcze jeden folder – Shared. Zawiera on widoki przeznaczone dla wszystkich kontrolerów. Jeśli zatem np. AccountController nie znajdzie stosownego widoku w Views\Account, znacznie go szukać w Views\Shared. Możemy w nim umieszczać części wspólne dla całego serwisu np. MasterPage lub fragmenty strony (panel logowania, menu).
Zobaczmy przykładową implementację kontrolera:
[HandleError] public class HomeController : Controller { public ActionResult Index() { ViewData["Message"] = "Welcome to ASP.NET MVC!"; return View(); } public ActionResult About() { return View(); } }
Oczywiście każda klasa kontrolera musi dziedziczyć po Controller. Przedstawiony HomeController zawiera 2 akcje: Index oraz About, które mapowane są na adresy WWW:www.domain.com/Home/Index i www.domain.com/Home/About. Zainteresowanych odsyłam do pliku Global.asax, w którym mapowania są zdefiniowane.
Użytkownik wpisując adres www.domain.com/Home/Index do przeglądarki wywołuje metodę Index. Programista może w niej wywoływać metody modelu lub warstwy biznesowej a następnie musi zwrócić odpowiednik widok. Metoda View zwraca domyślny widok dla akcji i kontrolera czyli dla metody Index jest to Views\Home\Index lub Views\Shared\Index jeśli ten pierwszy nie został znaleziony.
W następnym poście pokażę jak tworzyć widoki w ASP .NET MVC oraz co ofertują nam dostarczone helpery.
Warunkowe wyświetlanie kolumn w T-SQL
Załóżmy, że mamy tabelę składającą się z 4 kolumn: FirstName, LastName, CompanyName oraz PersonType. Pole PersonType przyjmuje ‘C’ gdy dany wiersz przedstawia firmę oraz ‘N’ gdy reprezentuje osobę fizyczną. Następnie chcemy wyświetlić w zależności od typu osoby nazwę firmy (C) lub imię+nazwisko(N). Zadanie można zrealizować za pomocą klauzuli case when:
select (case when PersonType='C' then CompanyName else FirstName+' '+LastName end) Name from Persons
Powyższy przykład miał tylko pokazać zastosowanie case when. W praktyce powyższa tabela nie jest zgodna z trzecią postacią normalną (3NF) i powinna zostać rozdzielona na dwie tabele (osoby fizyczne oraz firmy).