700 likes | 879 Views
Organizacja dostępu do danych w aplikacjach WWW. Tomasz Kopacz. Architect | Microsoft. Agenda. Definicja problemu Odczyt danych (dane relacyjne) AJAX,RSS i dane Zapis danych „Inne” typy danych Enterprise Library Rozproszona pamięć podręczna ( Velocity ) Uwagi o budowie warstwy DAL.
E N D
Organizacja dostępu do danych w aplikacjach WWW Tomasz Kopacz Architect | Microsoft
Agenda • Definicja problemu • Odczyt danych (dane relacyjne) • AJAX,RSS i dane • Zapis danych • „Inne” typy danych • EnterpriseLibrary • Rozproszona pamięć podręczna (Velocity) • Uwagi o budowie warstwy DAL
Aplikacje WWW - problemy • Dużo użytkowników • Nieznana liczba zapytań • I częstotliwość zapytań • Skalowalność bazy zawsze skończona • Użytkownik niezadowolony • Odejdzie, pójdzie do konkurencji, nie kupi • Opisze na blogu… • Dane to też pliki, XML, usługi…
„Klasyczna 3 warstwowa aplikacja” • Cel • Podział odpowiedzialności w kodzie • Niezależne testowanie • Możliwość ponownego użycia • Całości, koncepcji lub komponentów
Wymagania odnośnie DAL • Elastyczność • Ujednolicone i JEDYNE API; niezależne od „typu” danych • Różne bazy • Źródła nierelacyjne; Pliki; kolekcje • Konfiguracja i wdrażanie… • Czasami SZYBKOŚĆ/SKALOWALNOŚĆ najważniejsza • Wtedy elegancja i elastyczność mniej istotna
(Podstawy ADO.NET) • SqlConnection (OleDBConnection) • Wybór bazy, użytkownika • Pula i konsekwencje • Opcje (trzeba JAWNIE włączyć): • Asynchroniczne • MARS • SqlCommand (OleDbCommand) • Niezarządzalny a zarządzalnyprovider • Lokalizacja serwera DB a serwera WWW • SQL 200x: UserInstance i WWW – zły pomysł
<book> <title/> <author/> <year/> <price/> </book> Dane relacyjne Obiekty XML (Projekt LINQ) gridCategories.DataSource = fromcategoryindb.Production.ProductSubcategory wherecategory.Group==„Żywność”orderbycategory.ProductSubcategoryIDselectnew {CategoryID = category.ProductSubcategoryID,Name = category.Name }; .NET Language Integrated Query C# 3.0 VB 9.0 Inne LINQ toObjects LINQ toDataSets LINQ toSQL LINQ toEntities LINQ toXML
Proste strony ASP.NET • Sposoby wywołań • StoredProcedure • ASPX + kod inline; podobnie kreator • LINQtoEDM • LINQtoSQL • Binding do kontrolek – koszt pomijalny • Uwaga na ViewState • Rozmiar strony
Wyniki wydajności Przewaga SP wzrasta ze „skomplikowaniem” zapytania
Mars a lokalny cache Iteracja po 2 recordsetach, średnia z 10 minut, 3 równoległych użytkowników Iteracja po 2 recordsetach, średnia z 10 minut, 100 równoległych użytkowników Iteracja po 2 recordsetach, średnia z 10 minut, 500 równoległych użytkowników
Typy elementów asynchronicznych w ASP.NET • Asynchroniczne ASP.NET poprawia skalowalność przy dużym I/O • Można użyć • Strony asynchroniczne (najlepiej PageAsyncTask) • Asynchroniczne handlery HTTP (IHttpAsyncHandler) • Narzędzia w bibliotekach .NET • Asynchroniczne ADO.NET (BeginExecuteReader) • Asynchroniczne proxyWebServices (w WCF trzeba jawnie wygenerować)
Asynchroniczne ASP.NET • Jednostkowo strona MOŻE być wolniejsza • Lepsza skalowalność, ale większe zasoby Synchroniczne Asynchroniczne
Asynchroniczne ASP.NET v 1 protected void Page_Load([…]) { if (!IsPostBack) { AddOnPreRenderCompleteAsync(newBeginEventHandler (BAO1), newEndEventHandler (EAO1)); AddOnPreRenderCompleteAsync(new BeginEventHandler(BAO2), newEndEventHandler(EAO2)); […] IAsyncResultBAO1 (object sender, EventArgs e, AsyncCallbackcb, object state) { cmd1 = new SqlCommand(…); return cmd1.BeginExecuteReader(cb,state); } […] void EAO1(IAsyncResult ar) { datareader1= cmd1.EndExecuteReader(ar); gv1.DataSource = dr1; gv1.DataBind(); } • AddOnPreRenderCompleteAsync • Jeden wątek! • Kilka po kolei • Nie ma dostępu do stanu strony • Uwaga! Zawsze kosztem wątków roboczych w puli • Coś za coś…
Asynchroniczne ASP.NET v 2 protected void Page_Load([…]) { if (!IsPostBack) { RegisterAsyncTask(new PageAsyncTask( new BeginEventHandler(BeginAsyncOperation1), new EndEventHandler(EndAsyncOperation1), new EndEventHandler(Timeout1),null,true )); RegisterAsyncTask(new PageAsyncTask( new BeginEventHandler(BeginAsyncOperation2), new EndEventHandler(EndAsyncOperation2), new EndEventHandler(Timeout1),null,true )); ExecuteRegisteredAsyncTasks(); • RegisterAsyncTask • PageAsyncTask • Dowolna ilość wątków • (IHttpModule) • IHttpAsyncHandler • BeginProcessRequest • EndProcessRequest
Porównanie wydajności Asynchroniczne, AddOnPreRenderCompleteAsync : Minimalny czas odpowiedzi: 30,3 s Średni: 159 s Maksymalny: 298 s Asynchroniczne,PageAsyncTask : Minimalny czas odpowiedzi: 9,7 s Średni: 150 s Maksymalny: 297 s Przyrost 50 – 950 użytkowników Przyrost 50 – 950 użytkowników Synchroniczne: Minimalny czas odpowiedzi: 153 s Średni: 221 s Maksymalny: 298 s
To kiedy używać DataSet? • Możliwości • Offline • SZYBKA synchronizacja i ponowne połączenie • Porównanie wersji, przesłanie różnic • DiffGram • Scenariusz S+S / SmartClient • A nie strona WWW • Uwaga – DataSet i Cache • Czasami w warstwie DAL/BLL jako obiekt pomocniczy
To kiedy używać DataSet? W scenariuszu aplikacji WWW WCALE
AJAX • (bazuje na XMLHttpRequest) • Możliwości • Częściowy rendering strony (UpdatePanel) • Obejmuje standardowe kontrolki (nawet Web Parts) • Zamienia PostBack na wywołania AJAX-owe • AjaxControlToolkit / Extendery • Web Services zwracające JSON • Script Framework • Asynchroniczne po stronie klienta • Dane – Web Service; JSON(P)/XML
ASMX dla AJAX Kod ASMX: [System.Web.Script.Services.ScriptService] public class ZipCodeService : […] [System.Web.Services.WebMethod] public string[] GetBank(string z) Strona ASPX: <asp:ScriptManager ID="ScriptManager1" Runat="server"> <Services> <asp:ServiceReference Path=“BankService.asmx" /> </Services> </asp:ScriptManager> • Dodatkowy atrybut do normalnych funkcji Web Service • Referencja = automatyczne Intellisense + struktury do wywołania kodu • Też „PageMethods” • WCF • Atrybut WebGet i UriTemplate
Wywołanie serwisu BankService.GetBank(“781060[…]7", onCompleted,onFailed); […] function onCompleted (result) { window.alert(result); } function onFailed (err, context, methodName) { window.alert(err.get_message()); } • Używane XMLHttpRequest • Cross-Domain – problem • Rozwiązanie: klasa proxy po stronie serwera WWW • Zabawy z IFrame... • Serializacja XML/JSON • Uwaga na duże zbiory danych…
03 AJAX i dostęp do danych 03b RSS jako źródło danych
„Zapis normalny” • Synchroniczne • Zapis asynchroniczny – nie ma sensu • Zawsze schemat – request / response • Pokaż stronę po zmianach • Scenariusz • „Wyślij formularz” • „Dziękujemy za przesłanie formularza” • A najlepiej potwierdzić w jeszcze inny sposób: mail, SMS
Wykrywanie konfliktów • Niestety optymistyczny • Ostatni ma rację • Rozwiązania • Wersja rekordu w bazie • TimeStamp do ViewState • Nie zalecana sesja • ViewState „żyje” dłużej i jest po stronie klienta • Niebezpieczeństwo zafałszowania, ale…. • Uwaga – przechowując wersje, mechanizmy EDM/LinqtoSql – przydatne
Service Broker • Problem: duża (za duża) ilość poleceń • Rozwiązanie: kolejka • Przechowanie żądań • Inne możliwości Service Broker: routowanie pomiędzy serwerami/bazami danych • A wszystko w kontekście transakcyjnym!
Konfiguracja Service Broker • Schemat komunikatu (typ XML) • Typ komunikatu (dla Request i Response) • Kontrakt (jaki komunikat wysyłany a jaki odbierany) • Definicja kolejki • Definicja serwisu (para – kolejka i kontrakt) • Związanie z kolejką procedury SP wołanej po przyjściu komunikatu • I określenie limitu liczby wywołań • Ew. odesłanie informacji zwrotnej
Długie transakcje • Scenariusze: Koszyk; dokument wielostronicowy • (kilka operacji Postback) • Rozwiązania • ViewState pomiędzy stronami • Ciastko + guid + znakowanie transakcji • Ew. sesja • Potem proces „czyszczący” • Problemy • WorkflowFoundation • Klasa jako pojemnik na gromadzone informacje • Odmiana – Page Flow z Web Client Software Factory
„Inne” typy danych (wykorzystując SQL 2008)
HierarchyId • Wbudowane metody do manipulacji hierarchiami • Na przykład: GetAncestor , GetDescendant , GetLevel • INSERT #Hierarchy VALUES ('Debbie Walker','/1/2/') • Indeksy depth-first i breadth-first • Nadal jest to baza RELACYJNA • Nie ma typów w Entity Framework / LinqToSql / DataSetitp…
XML • 2 zastosowania: • Mapowanie XML <-> relacyjne • Baza „hierarchiczna” w bazie relacyjnej • Klient: • SqlXml (System.Data.SqlTypes) • XmlReaderz sqlxml.CreateReader() • DataSet – typ pola XPathDocument • Usługa Web itp. • Typ kolumny/zmiennej • Dokument XML 1.0 lub fragment • Przechowywane: • Jako LOB (2 GB), binarnie • Zakodowane w UTF-16 • W3CSchema / bez schematu • Ładnie się łączy z SQL! • Też SP: sql:variable(“@xml”) • CAST/CONVERT • Indeksy • CREATE PRIMARY XML INDEX idx_1 ON docs (xDoc) • Dodatkowe indeksy na wartościach, (VALUE), ścieżkach (PATH), właściwościach (PROPERTY) • Laxvalidation (anyType), xs:DateTime… • XQuery • query(), value(), exist() • SELECT id, xDoc.query ('for $s in /doc[@id = 123]//section…FROM docs • let $count :=count($invoice/Items/Item) • modify() (XML DML) • UPDATE docs SET xDoc.modify('insert<section num=''2''>… • after (/doc/section[@num=1])[1]‘)
SQL 2008 - Filestream CREATE DATABASE [MTS2008] ON PRIMARY ( NAME = N'MTS2008', FILENAME = N‘[…] MTS2008.mdf' , […]), FILEGROUP [FileStreamGroup1] CONTAINS FILESTREAM DEFAULT ( NAME = N'Arch3', FILENAME = N'C:\SQL2008ENTRTM\MTS2008' ) LOG ON ( NAME = N'MTS2008_log', FILENAME = N‘[…]' , […]) CREATE TABLE [dbo].[tPictInFilestream]( [id] [int] IDENTITY(1,1) NOT NULL, [gid] [uniqueidentifier] ROWGUIDCOL NOT NULL, [name] [nvarchar](50) NOT NULL, [pict] [varbinary](max) FILESTREAM NULL) INSERT INTO [tPictInFilestream] ([name],pict) VALUES (@name,cast('' as varbinary(max))) SELECT id,pict.PathName() fromtPictInFilestream
FileStream - klient • Otworzyć transakcję • Pobrać GET_FILESTREAM_TRANSACTION_CONTEXT • Pobrać .PathName() do pliku • Stworzyć fs=newSqlFileStream(path, tx, tryb otwarcia) • Operacje na fs jak na normalnym strumieniu Albo (tylko lokalny serwer): 3.5 : Przetworzyć ścieżkę z udziału sieciowego na adres lokalny (C:/...) 4 – 5: Pracować z normalnymi strumieniami .NET Tracimy część transakcyjności! „Tricky” – wersjonowanie; niezalecane (chyba że początkowy wsad danych)
EnterpriseLibraryData CachingPolicy Injection
Zadania • EnterpriseLibrary: Zbiór „bloków” ułatwiających wykonywanie pewnych zadań, tu: • Data Access Application Block • CachingApplication Block • Policy InjectionApplication Block • I oczywiście - konfiguracja
Konfiguracja • Delta (Environments) • Zmiany • Merge do „nowego” .config • Także dla „zwykłych” wpisów • Też linia poleceń:
DAAB - Scenariusze • Ułatwienie w warstwie DAL • Dodatkowa izolacja od typu źródła • Z dokładnością do składni SQL… • Konfiguracja w jednym miejscu • Krótszy kod - jednolinijkowce • Naprawdę krótszy! • Łatwiej mieć niezależność od bazy • Zwłaszcza – przy używaniu procedur • Automat zarządzający czasem życia połączenia • + Pula (jako mechanizm dodatkowy)
DAAB - Operacje • Komunikacja za pośrednictwem ADO.NET: • Używa DBConnection/DBCommand • SQL Server, ODBC,OleDBB,OracleClient,SqlServerCe • Operacje: • Wywołanie SQL/SP • Zwrócenie DataReader, DataSet, XML, wyniku polecenia • Cache metadanych -> SZYBKO DZIAŁA • Transakcje • Sensownie używa TransactionScope • Bez niepotrzebnej eskalacji do DTC • Aktualizacje • Model DataSet/DataAdapter • UpdateBehavior • UpdateDatasetma paramertupdateBatch
CachingApplication Block • Cel – przechowanie danych w pamięci podręcznej • Pojemniki: • Null – pamięć • IsolatedStorage • DAAB (zwykle lokalny SQL) • Zwykle – element ma czas życia • ICacheItemExpiration • Własne reguły wygaszania • ICacheItemRefreshAction • Co się dzieje gdy element wygaśnie • WWW: jest też HttpRuntime.Cache
Policy Injection Block [CachingCallHandler(0, 0, 30)] public decimal GetSavingsBalance(intaccountNumber) Lub [Tag("MyCache")] public decimal GetSavingsBalance(intaccountNumber) […] OrderTransorders = PolicyInjection.Create<OrderTrans>(); • „Wstrzykiwanie” funkcjonalności • Modyfikuje tworzenie obiektów • Dodaje pewne „zachowania” za pośrednictwem handlerów: Logging, Validation, Caching… • Reguła określa kiedy handlery są stosowane