Sunteți pe pagina 1din 429

1. Pentru o aplicatie WPF, care atribut referentiaza un namespace in fisierul XAML?

- Xmlns

2. Care din urmatoarele reprezinta manageri de continut in WPF?


- Grid
- WrapPanel
- Canvas

3. Care din urmatoarele afirmatii este adevarata referitor la XAML?


- Separata design-ul de logica aplicatiei

4. Care din urmatoarele afirmatii este adevarata?


- eXtensible Application Markup Language este un limbaj declarativ
- Fiecare element din XAML reprezinta o clasa in .NET
- Atributele intr-un fisier XAML reprezinta o proprietate, metoda sau eveniment in cadrul unei
clase .NET

5. Care din urmatoarele afirmatii sunt adevarate referitor la paginile Razor?


- Pagina Layout furnizeaza un Layout unitar pentru intreaga aplicatie
- Pagina Layout permite containerului HTML pentru layout sa fie specificat intr-un singur loc
- Pagina Layout permite containerului HTML pentru layout sa fie utilizat pentru pagini multiple

6. Care din urmatoarele tiuri de data binding exista in WPF?


- OneWay
- TwoWay
- OneWayToSource
- OneTime

7. Obiectele de tipul IcollectionView suporta:


- Sortare
- Filtrare
- Grupare

8. Care clasa este utilizata pentru data binding in WPF?


- Binding

9. Evenimentele suportate in WPF sunt:


- evenimente directe
- evenimente de tip bubbling
- evenimente de tip tunel

10. Care din urmatoarele afirmatii este adevarata?


- Comenzile WPF pot fi aplicate mai multor tipuri de controale
11. Care este prima directiva care apare intr-o pagina Razor?
- @page

12. Care din urmatoarele nu repezinta un mecanism pentru gestiunea starii in ASP.NET Core?
- Cookies

Medii

Care din urmatoarele afirmatii este adevarata in cee ace priveste XAML
ul?
Separarea codului de design si a logicii programului

Care dintre urmatoarele afirmatii este adevarata legata de proprietatea de


dependenta?
Proprietatiile de dependenta apartin unei singure clase dar, pot fi folosite
in altele.

Ce triggers sunt disponibili in WPF(windows presentation foundation)?


Property triggers
Data triggers
Event triggers
Care din urmatoarele afirmatii este adevarata?
Triggers urile se pot folosi/merg cu Style
Care din urmatoarele afirmatii este adevarata?
eXtensible Application Markup Language este un limbaj declarative
fiecare element dintr un XAML file reprezinta o clasa in .NET
atributul care se afla intr un xaml file reprezinta o proprietate,o metoda,
sau un event intr o plasa .NET

Care atribut in WPF face referinta la un namespace in fisierul XAML?


Xmlns

Care obiect poate fi folosit pentru a share ui/ imparti variabilele in


paginile WPF?
Application

In ce namespace este valabil brush class?


System.Windows.Media

Transformarile WPF pot fi folosite pentru


Rotatie,scaling si distorsionare/manipulare

Care proprietate poate fi folosite entru a face controlul semi transparent?


OpacityMask

Care dintre urmatoarele au 2 stari pentru un obiect freezable ?


Frozen and unfrozen
Ce control poate fi folosit pentru a seta proprietatea controlul cand dai
run time?
PropertyGrid

Pentru a arata dial box in mod modal care metoda este folosita?
ShowDialog

Aplicatia WPF poate sa fie deployed/desfasurata de?


ClickOnce

Care din urmatoarele este necesara pentru a incepe un process in


backround cu componenta BackgroundWorker?
Apeland metoda RunWorkerAsync si Handling event ului DoWork

Care controller este folosit pentru a permite folosirea unui pattern


prestabilit pentru acceptarea sau respingerea inputului de la user ?
MaskedTextBox

Care din urmatoarele este adevarata?


WPF suporta resurse binare si resurse logice

Care clasa este folosita pentru data binding in WPF?


Binding
ICollectionView suporta:
Sortare
Filtrare
Grupare

Pentru facilitatea drag and drop, ce clasa se foloseste?


DragDrop

Pentru transformarea 2D WPF suporta clasele:


RotateTransform
ScaleTransform
SkewTransform

Care namespace uri ofera clasele pentru integrarea cu WPF si Win32?


System.Windows.Interop

Ce template uri sunt suportate de WPF?


Data Templates
- Control Templates
- Items Panel Templates
Care sunt tipurile diferite de Data Binding Modes in WPF?
1)OneWay 2) TwoWay 3) OneWayToSource 4) OneTime
1,2,3,4

Care sunt cele doua tipuri de resurse logice disponibile in WPF?


StaticResource and Dynamic Resource

Care dintre urmatorii DataProvider foloseste XML ca un DataSource?


XmlDataProvider

Care din urmatoarele parti XAML arata imaginea unui buton de control?
< Button> < StackPanel Orientation="Horizontal"> < Image
Source="speaker.png" Stretch="Uniform"/> < TextBlock Text="Play
Sound" /> < /StackPanel> < /Button>

Pentru a reda un file .wav, ce clasa se foloseste?


SoundPlayer

Care brush este suportat de WCF(Windows communication foundation)


?
RadialGradientBrush
Care controller layout ar fi cea mai buna alegere pentru o interfata a
utilizatorului care necesita spatiu egal ….
UniformGrid
Care dintre urmatoarele este un layout control ?
Grid – Canvas -UniformGrid

How you can set a working mnemonic key for a label?


Set the Target property to the target control. Precede the letter for the
mnemonic key with an underscore symbol in the Content property of the
Label control.

How many child controls can a content control contain?


1

Care este tipul lui Content property?


Obiect

Which of the following is TRUE?


1.Tunneling Event is raised first by the topmost container. 2
2.PreviewMouseDown is Tunneling Event
3.PreviewKeyDown is Tunneling Event
4.MouseDown is Bubbling Event
Raspuns 1,2,3,4
Care dintre urmatoarele afirmatii este adevarata?
Bubbling event sunt ridicate mai intai in controller ul din care
provin(origineaza)

Event urile sportate de WPF sunt:


Direct Event
- Bubbling Event
- Tunneling Event

Care dintre urmatoarele pot hosta o pagina?


Internet Explorer
- Navigation Window
- Frame

Care dintre urmatoarele bucati de cod, este corecta?


< TextBlock>This is my < HyperLink
NavigateUri=”MyPage.xaml”>Hyperlink< / Hyperlink>< /TextBlock>

Cum vei arata o noua instantaa clasei MyWindow?


MyWindow obj=new MyWindow(); Obj.Show();

Care dintre urmatoarele operatii sunt posibile in XBAP?


Nici una
Ce obiect foloseste Navigation Application pentru a arata Navigation
Window?
Page

Ce tipuri de aplicatie suporta WPF?


- Windows Application
- Navigation Application
- XBAP

In WPF ce limbaj este folosit pentru a arata user interface?


XAML

WPF este suportat de :


.NET Framework 3.0 si versiunile main oi

ADO.NET
Care din urmatoarele afirmatii nu este adevarata legata de ADO.NET?
ADO.NET nu suporta arhitectura deconectata.

Care dintre urmatoarele ilustreaza beneficiile ADO.NET ului?


Interoperability –
It uses DataSet to represent data in memory that can store data from
multiple tables and multiple sources.
- Disconnected data access
Care dintre urmatoarele este un data provider pentru un obiect .NET?
- Connection
- Command
- Data Reader

Obiectul DataAdapter populeaza un DataSet si rezolva update uriele cu


data source.
DA

Cand creezi o relatie intre tabele in ADO.NET:


Un UniqueConstraint este adaugat in tabela parinte
Un ForeignKeyConstraint este adaugat in tabelul copil (automat)

Care dintre urmatoarele sunt adevarate legate de ADO.NET DataSet?


DataSet provides a disconnected view of a data source.
- Dataset enables to store data from multiple tables and multiple sources
- We can create relationship between the tables in a DataSet.

Which of the following event(s) is/are exposed by the DataTable object?


- RowChanged
- ColumnChanged
- RowChanging
Care dintre urmatoarele nu este o metoda DataAdapter?
ReadData

Care dintre urmatoarele este o metoda furnizata de obiectul DataSet


pentru a genera obiectul XML?
- ReadXML
- WriteXML
- GetXML

Care din urmatoarele nu este un limaj compatibil cu .NET?


Java

Common language specification(CLS)


Defineste reguli standard pentru definirea limbajelor compilatoare .NET

Ce reprezinta CTS in .NET?


Reguli referitoare la tipuri de date

Ce fel de tipuri sunt suportate de CTS?


Value, References Types

Care din urmatoarele afirmatii nu este corecta?


.NET ofera cross language interoperability folosind code access security

Content of assembly can be viewed using


ILDASM.exe

Afirmatia using or Imports:


Folosirea referintei catre o librarie externa

Care din urmatoarele nu e adevarata despre o structura .NET?


Structurile sunt tipuri de referinta

Putem controla cand un destructor e apelat?


Nu.

Care din urmatoarele afirmatii e corecta pentru un garbage collection?


Poti fi sigur cand un obiect va fi revendicat de catre gc.

Care din urmatoarele afirmatii nu e corecta pentru o exceptie .NET?


Exceptiile nu pot fi generate programmatic.

Cand se foloseste gc?


Cand aplicatia mai are putina memorie.

Serverul remote .NET trebuie sa implementeze


Iserializable

Limitarea serializarii XML


Nu serializeaza type information.

Caracteristici ale managementului memorii automate in .NET


-Alocarea memoriei
-Eliberare de memorie
-Implementing finalizers

Care e cel mai bun mod de a depozita connection strings?


Config files

In care eveniment sunt controalele incarcate full?


Page_Load

Care este numele proprietatii paginii ASP.NET ce poate fi interogata ca sa


determini daca o pagina ASP.NET…….
IsPostBack
Pentru a separa cod server-side de cod client-side intr-o pagina ASP, ce model de
programare ar trebui folosit?
Code-Behind model

Pentru a performa acces de data asincron……


Asynchronous=true

Intr-o aplicatie ASP vrei sa afisezi o lista de client intr-o pagina web. Lista de clienti
afiseaza 10 clienti deodata, si ti se cere sa editezi client. Care web control e cel
mai pottrivit?
GridView control

Cum se implementeaza autentificarea via web.config?


Include elementul de autentificare

Session_End fires in which of these SessionState modes?


Inproc

You are developing a web application that is retrieving historical library


information from database server and displays it to the users of your
application. What cache strategy will give you the best performance?
Folosirea unui obiect cache

Ce obiect ASP.NET encapsuleaza starea unui client si a browserului?


Session Object

Vrei sa stochezi datele care sunt accesibile pentru orice user care se conecteaza la
aplicatia ta Web. Care obiect folosesti?
Application

Care clasa poate fi folosita pentru a crea XML de la inceput ?


XmlDocument
Care clasa poate fi folosita pentru a face conversia intre tipurile de data .Net si
XML?
XmlConvert

In aplicatia voastra web, ASP.NET 2.0 vrei sa afisezi o imagine care este selectata
dintr o colectie de imagini. Cum veti face aceasta?
Folosim AdServer control sic reem un fisier XML, care contine configuratia
controaler ului

Care dintre urmatoarele este necesara cand combini datele intr un DataSet?
Un primary key trebuie sa fie definit in obiectele DataTable

Ce tip de date putem stoca in viewstate?


Viewstate poate fi stocata doar in obiectele serializate.

Care sunt/este TraceListener predefinit in ASP .Net?


TextWriterTraceListener
- EventLogTraceListener
- DefaultTraceListener

Am definit un event page_load intr o pagina aspx si acelasi page_load in code


behind, care dintre acestea va rula primul?
Page_load event in code-behind

Care dintre urmatoarele reprezinta cel mai bine folosirea lui: Table, TableRow si
Table-Cell controls?
Pentru a creea un cotroaler customizat pentru a afisa datele in mod tabular

Cum poti determina care versiune de ASP.NET ruleaza pe system ul tau?


Response.Write(System.Environment.Version.ToString() );

Care este diferenta dintre Response.Write() si Response.Output.Write()?


Response.Output.Write() iti permite sa afisezi output ul formatat
Where can we assign value to Static read only member variable of a static class?
Default constructor

Care dintre urmatoarele fisiere iau un web application in modul offline?


App_offline.htm

Care dintre aceste clase mapeaza la tag ul <input type = “”checkbox”/>


HtlmInputCheckBox

Pentru a preveni browser ul de la a face caching paginii care dintre aceste


statement uri ar trebui sa fie scrise?
Response.Cache.SetNoStore();

Care dintre dintre aceste data sources controls nu implementeaza Caching?


LinqDataSource
Curs 1
Introducere în .NET, WPF, XAML

Lect. dr. Florina Covaci


Obiective
• Deprinderea principalelor concepte ale programării în .NET
Framework 4.8 și .NET Core 3.1
• Programarea aplicatiilor Windows utilizand WPF
• Programarea aplicatiilor Web utilizand ASP.NET Core
• Programarea aplicatiilor mobile utilizand Xamarin.Forms
• Programarea accesului la date

Echipa:
• Lect. Dr. Florina Covaci, Asist. Dr. Paula Zalhan

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Bibliografie

Andrew Troelsen, Philip Japikse - Pro C# 7 with .NET and .NET Core
Christian Nagel - Professional C# 7 and .NET Core 2.0

Platforma de elearning:
econ.elearning.ubbcluj.ro/moodle

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Evaluare
• Probă teoretică: 40%
• Examen scris în sesiune

• Proiect individual: 40%


• Examinat prin sustinere în perioada 10-16 ian. 2020

• Promovare examen: minim 5 la fiecare probă de examen

• Activitate laborator 20%

MEDII DE PROGRAMARE ȘI DEZVOLTARE


De ce VS și C#?

https://insights.stackoverflow.com/survey/2020
MEDII DE PROGRAMARE ȘI DEZVOLTARE
Istoria .NET (I)
• 2002 .NET framework 1.0 – C# 1.0
• C# 1.0 un nou limbaj de programare dezvoltat pentru .NET
framework - Anders Hejlsberg
• .NET framework continea 3000 de clase la momentul cand a
fost creat

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Istoria .NET (II)
• 2005 .NET framework 2 - C# 2
• Generics – crearea de parametrii generici fara a se declara tipul
acestora

• 2005 .NET framework 3


• WPF , WCF, WF – noi librarii

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Istoria .NET (III)
• 2008 .NET 3.5 - C# 3
LINQ -interogari folosind aceeasi sintaxa pentru colectii de obiecte,
baze de date si fisiere XML
• 2010 .NET 4 - C# 4
facilitati noi pentru programare paralela
• 2012 .NET 4.5 - C#5
programare asincrona – device-uri cu touch screen

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Istoria .NET (IV)
• 2015 C#6 and .NET core 1.0
runtime distribuit cu fiecare aplicatie, open source, multi-platforma
• 2017 C#7 and .net core 2.0
noi facilitati pentru programarea functionala
• 2019 C#8 and .Net core 3.0
îmbunatatiri pentru programarea functionala si asincrona

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Tehnologii curente .NET

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Aplicatii de tip Desktop

• Aplicația nu are nevoie de conexiune la internet


• O mai buna interactiune cu utilizatorul
• Aplicatiile desktop pot oferi o performanță mai buna decât aplicațiile
web
• Desi e posibila rularea de algoritmi complecsi in partea de client a
aplicatilor web, acest lucru se realizeaza cu dificultate
• Utilizarea firelor de execuție este mai facila în aplicațiile Desktop

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Clasa, Namespace, Assembly

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Windows Presentation Foundation & XAML
• WPF oferă posibilitatea de a separa interfata de logica de programare
• Interfata este definita folosind XAML markup iar apoi acest markup
poate fi conectat la un fisier cu cod C# (code-behind) pentru a furniza
funcționalitatea interfeței
• XAML permite definire de elemente simple (butoane, griduri,liste,
etc.) dar și grafică interactivă 2D și 3D, animații, functionalitati
multimedia (rulare video)

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Namespace-ul System.Windows
• Este namespace-ul principal al WPF

• Contine două din clasele principale folosite în orice proiect WPF


• Application
• Window

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Clasa Application
• App.xaml.cs

• Obiect din clasa Application - o instanță a unei aplicații WPF care


rulează

• Furnizează:
• metoda Run() pentru a porni aplicația

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Proprietati ale clasei Application

• StartupUri – setează sau furnizează valoarea pentru un URI care specifică fereastra care se
deschide automat când o aplicație pornește
• Current –permite accesul din orice parte a aplicatiei la obiectul aplicație care rulează
• Windows – asigură acces la o colecție care reprezintă ferestrele din memorie pentru
aplicația WPF curentă. Pe masură ce se crează noi obiecte de tip fereastră, ele sunt
adaugate in colectia Application.Windows

static void MinimizeAllWindows()


{
foreach (Window wnd in Application.Current.Windows)
{
wnd.WindowState = WindowState.Minimized;
}
}

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Clasa Window
• Un obiect din clasa System.Windows.Window reprezintă o fereastră
deținută de obiectul din clasa Application (derivată)

• System.Windows.Controls.ContentControl – poate găzdui o singură


piesă de conținut, care se referă la datele vizuale plasate în interiorul
suprefeței controlului prin intermediul proprietății Content

<Button Height="80" Width="100" Content="OK"/>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Conținut complex
• Conținut în interiorul elementului
<Button Height="80" Width="100">
<StackPanel>
<Ellipse Fill="Red" Width="25" Height="25"/>
<Label Content ="OK!"/>
</StackPanel>
</Button>

• Conținut specificat prin proprietatea elementului


<Button Height="80" Width="100">
<Button.Content>
<StackPanel>
<Ellipse Fill="Red" Width="25" Height="25"/>
<Label Content ="OK!"/>
</StackPanel>
</Button.Content>
</Button>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


System.Windows.Controls.Control
• Toate controalele WPF mostenesc clasa de bază Control

• Furnizează proprietățile de bază :


• marimea elementului,
• opacitate,
• ordinea de accesare a controalelor,
• culoare fundal
• etc.

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Namespaces și cuvinte cheie XAML
• Elementul rădăcină a unui document WPF XAML (<Window>, <Page>,
<UserControl>,<Application>) conține referințe către urmatoarele
două namespace-uri predefinite:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
</Grid>
</Window>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


XML Namespaces
• http://schemas.microsoft.com/winfx/2006/xaml/presentation mapează System.Windows,
System.Windows.Controls, System.Windows.Data, System.Windows.Ink, System.Windows.Media,
System.Windows.Navigation, etc.
• http://schemas.microsoft.com/winfx/2006/xaml este utilizat pentru a include cuvinte cheie XAML
• Regula: elementul rădăcină specifică un namespace XML ca fiind namespace-ul primar , fiind cel
din care se utilizează cele mai multe elemente. Dacă este necesar[ includerea unor alte
namespace-uri secundare, acestea trebuie definite utilizând un prefix (prin convenție acest prefix
este de obicei x, dar poate fi orice prefix (de ex. XamlSpecificStuff)
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:XamlSpecificStuff="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
</Grid>
</Window>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


XML Namespaces
• Descrierea unei clase .NET dintr-un assembly extern
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myCtrls="clr-namespace:MyControls;assembly=MyControls"
Title="MainWindow" Height="350" Width="525">
<Grid>
<myCtrls:MyCustomControl />
</Grid>
</Window>

xmlns:myCtrls="clr-namespace:SomeNamespaceInMyApp" - clasa se află în


acelasi assembly, dar în alt namespace
MEDII DE PROGRAMARE ȘI DEZVOLTARE
XAML: Elemente, Atribute și Convertori de tip
<Window public void MakeAButton()
xmlns="http://schemas.microsoft.com/winf
x/2006/xaml/presentation" {
xmlns:x="http://schemas.microsoft.com/wi Button myBtn = new Button();
nfx/2006/xaml">
<Grid> myBtn.Height = 50;
<Button Height="50" Width="100" myBtn.Width = 100;
Content="OK!"
FontSize="20" Background="Green" myBtn.FontSize = 20;
Foreground="Yellow"/> myBtn.Content = "OK!";
</Grid>
</Window> myBtn.Background = new
SolidColorBrush(Colors.Green);
myBtn.Foreground = new
SolidColorBrush(Colors.Yellow);
}
XAML – valori complexe pentru atribute

Property element syntax


public void MakeAButton() <Button Height="50" Width="100"
{ Content="OK!"
... FontSize="20" Foreground="Yellow">
// A fancy brush for the background. <Button.Background>
LinearGradientBrush fancyBruch = <LinearGradientBrush>
new <GradientStop Color="DarkGreen"
LinearGradientBrush(Colors.DarkGreen, Offset="0"/>
Colors.LightGreen, 45); <GradientStop Color="LightGreen"
myBtn.Background = fancyBruch; Offset="1"/>
myBtn.Foreground = new </LinearGradientBrush>
SolidColorBrush(Colors.Yellow); </Button.Background>
} </Button>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Property element syntax
<DefiningClass>
<DefiningClass.PropertyOnDefiningClass>
<!-- Value for Property here! -->
</DefiningClass.PropertyOnDefiningClass>
</DefiningClass>
• ----------------------------------
<Button Height="50" Content="OK! " FontSize="20" Foreground="Yellow">
...
<Button.Width>
100
</Button.Width>
</Button>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


XAML Attached properties
• Permit unui element copil sa seteze valoarea pentru o proprietate
care este definita intr-un element parinte – pozitionare elemente UI
într-unul din managerii de layout (Grid, DockPanel, etc.)
<ParentElement>
<ChildElement ParentElement.PropertyOnParent = "Value">
</ParentElement>

<Canvas Height="200" Width="200" Background="LightBlue">


<Ellipse Canvas.Top="40" Canvas.Left="40" Height="20" Width="20" Fill="DarkBlue"/>
</Canvas>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Fișierul App.xaml
• ferestra cu care pornește aplicatia StartupUri
<Application x:Class="WpfTesterApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentat
ion"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTesterApp"
StartupUri="MainWindow.xaml" Startup="App_OnStartup"
Exit="App_OnExit" >
<Application.Resources>
</Application.Resources>
</Application>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


App.xaml.cs
public partial class App : Application
{
private void App_OnStartup(object sender, StartupEventArgs e)
{
}
private void App_OnExit(object sender, ExitEventArgs e)
{
}
}

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Main()
• App.g.cs
public static void Main() {
WpfTesterApp.App app = new WpfTesterApp.App();
app.InitializeComponent();
app.Run();
}

public void InitializeComponent() {


this.Startup += new System.Windows.StartupEventHandler(this.App_OnStartup);
this.Exit += new System.Windows.ExitEventHandler(this.App_OnExit);
this.StartupUri = new System.Uri("MainWindow.xaml", System.UriKind.Relative);
}

MEDII DE PROGRAMARE ȘI DEZVOLTARE


WPF Styles
• Aspect unitar al controalelor dintr-o aplicatie – inaltime, latime, culoare de
background, culoare font etc.
• Un stil – un obiect care mentine o colectie de perechi de tipul proprietate-
valoare
• Descris ca o resursa la nivel de aplicatie in App.xaml
• Clasa System.Windows.Style – proprietatea Setters
• Triggers – permite captarea diverselor evenimente pentru a conditiona
aplicarea unor stiluri
• BasedOn – permite costruirea unui nou stil pe baza unui stil existent
• TargetType – permite aplicarea unor constrangeri care defnesc unde se
aplica stilul

MEDII DE PROGRAMARE ȘI DEZVOLTARE


TargetType
• <Style x:Key ="BigGreenButton" TargetType="Button">
• <Setter Property = "FontSize" Value ="20"/>
• <Setter Property = "Height" Value = "100"/>
• <Setter Property = "Width" Value = "100"/>
• <Setter Property = "Background" Value = "DarkGreen"/>
• <Setter Property = "Foreground" Value = "Yellow"/>
• </Style>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


BasedOn
• <Style x:Key ="TiltButton" TargetType="Button" BasedOn =
"{StaticResource BigGreenButton}">
• <Setter Property = "Foreground" Value = "White"/>
• <Setter Property = "RenderTransform">
• <Setter.Value>
• <RotateTransform Angle = "20"/>
• </Setter.Value>
• </Setter>
• </Style>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Triggers
• <Style x:Key = "GrowingButtonStyle" • <DoubleAnimation From = "40" To =
TargetType="Button"> "200"
• <Setter Property = "Height" Value = • Duration = "0:0:2"
"40"/> AutoReverse="True"/>
• <Setter Property = "Width" Value = • </Storyboard>
"100"/> • </BeginStoryboard>
• <Style.Triggers> • </Trigger.EnterActions>
• <Trigger Property = "IsMouseOver" • </Trigger>
Value = "True">
• <Trigger.EnterActions> • </Style.Triggers>
• <BeginStoryboard> • </Style>
• <Storyboard TargetProperty =
"Height">

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Curs 2
Manageri de continut
Delegates, Evenimente, Expresii Lambda
Lect. dr. Florina Covaci
Manageri de conținut
• Gestiunea poziției controalelor în fereastră
• Gestionează comportamentul controalelor când
utilizatorul redimensionează fereastra
• controlul își păstrează poziția în care a fost plasat la
design-time

• controlul se repoziționează orizontal/vertical

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Manageri de conținut
• Implicit când se creează o fereastă WPF ->Grid
<!- Butonul se afla în centrul ferestrei->
<Window x:Class="MyWPFApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/pres
entation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Fun with Panels!" Height="285" Width="325">
<Button x:Name="btnOK" Height = "100" Width="80"
Content="OK"/>
</Window>
Elemente multiple în scopul unei ferestre – eroare la compilare

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Canvas
• Poziționare absolută a controalelor; controalele raman acolo unde au fost plasate la
design time
• Redimensionare a feresterei (mai mica decat Canvas)- continutul nu va fi vizibil decat
dupa ce mărimea va fi >=Canvas
<Canvas Background="LightSteelBlue">
<Button x:Name="btnOK" Canvas.Left="212" Canvas.Top="203" Width="80"
Content="OK"/>
<Label x:Name="lblDetalii" Canvas.Left="17" Canvas.Top="14" Width="328"
Height="27" FontSize="15" Content="Detalii persoana"/>
<Label x:Name="lblNume" Canvas.Left="17" Canvas.Top="60" Content="Nume"/>
<TextBox x:Name="txtNume" Canvas.Left="94" Canvas.Top="60" Width="193"
Height="25"/>
<Label x:Name="lblPrenume" Canvas.Left="17" Canvas.Top="109" Content="Prenume"/>
<TextBox x:Name="txtPrenume" Canvas.Left="94" Canvas.Top="107" Width="193"
Height="25"/>
<Label x:Name="lblStudii" Canvas.Left="17" Canvas.Top="155" Content="Studii"/>
<TextBox x:Name="txtStudii" Canvas.Left="94" Canvas.Top="153" Width="193"
Height="25"/>
</Canvas>
MEDII DE PROGRAMARE ȘI DEZVOLTARE
WrapPanel
• Permite definirea de conținut care își va modifica poziția pe
măsură ce fereastra este redimensionată
<WrapPanel Background="LightSteelBlue“ >
<Label x:Name="lblInstructions" Width="328" Height="27“
FontSize="15" Content="Detalii persoana"/>
<Label x:Name="lblNume" Content="Nume"/>
<TextBox x:Name="txtNume" Width="193" Height="25"/>
<Label x:Name="lblPrenume" Content="Prenume"/>
<TextBox x:Name="txtPrenume" Width="193" Height="25"/>
<Label x:Name="lblStudii" Content="Studii"/>
<TextBox x:Name="txtStudii" Width="193" Height="25"/>
<Button x:Name="btnOK" Width="80" Content="OK"/>
</WrapPanel>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


StackPanel
• Similar cu WrapPanel
• Wrap vs. Strech
<StackPanel Background="LightSteelBlue" Orientation="Horizontal">
<Label x:Name="lblInstructions" Width="328" Height="27“
FontSize="15" Content="Detalii persoana"/>
<Label x:Name="lblNume" Content="Nume"/>
<TextBox x:Name="txtNume" Width="193" Height="25"/>
<Label x:Name="lblPrenume" Content="Prenume"/>
<TextBox x:Name="txtPrenume" Width="193" Height="25"/>
<Label x:Name="lblStudii" Content="Studii"/>
<TextBox x:Name="txtStudii" Width="193" Height="25"/>
<Button x:Name="btnOK" Width="80" Content="OK"/>
</StackPanel>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Grid
• Definirea si configurarea fiecarei coloane.
• Definirea si configurarea fiecarui rând. <Label x:Name="lblInstruction" Grid.Column
• Atribuirea de conținut fiecarei celule folosind ="0" Grid.Row ="0" FontSize="15"
attached property Content="Detalii persoana"/>
<Button x:Name="btnOK" Height ="30"
Grid.Column ="0" Grid.Row ="0" Content="OK"/>
<Grid ShowGridLines ="True"
Background ="LightSteelBlue"> <Label x:Name="lblNum" Grid.Column ="1"
Grid.Row ="0" Content="Nume"/>
<!- Define the rows/columns ->
<TextBox x:Name="txtNum" Grid.Column
<Grid.ColumnDefinitions> ="1" Grid.Row ="0" Width="193" Height="25"/>
<ColumnDefinition/> <Label x:Name="lblPrenum" Grid.Column
<ColumnDefinition/> ="0" Grid.Row ="1" Content="Prenume"/>
</Grid.ColumnDefinitions> <TextBox x:Name="txtPrenume"
Width="193" Height="25" Grid.Column ="0"
<Grid.RowDefinitions> Grid.Row ="1" />
<RowDefinition/>
<RowDefinition/> <Rectangle Fill ="LightGreen" Grid.Column ="1"
Grid.Row ="1" />
</Grid.RowDefinitions>
Grid(II)
• Dimensionarea liniilor si coloanelor:
• Absoluta (ex. 100)
• Autodimensionare
• Relativa(ex. 3x)

• GridSplitter – permit utlilizatorilor sa redimensioneze liniile sau


coloanele
<GridSplitter Grid.Column ="0" Width ="5" Grid.RowSpan="2"/>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


DockPanel
<DockPanel LastChildFill ="True" Background="AliceBlue">
<Label DockPanel.Dock ="Top" Name="lblInstruction" FontSize="15"
Content="Detalii persoana"/>
<Label DockPanel.Dock ="Left" Name="lblNu" Content="Nume"/>
<Label DockPanel.Dock ="Right" Name="lblPrenu"
Content="Prenume"/>
<Label DockPanel.Dock ="Bottom" Name="lblSt" Content="Studii"/>
<Button Name="btnOK" Content="OK"/>
</DockPanel>
• La redimensionarea ferestrei, fiecare element ramane pe partea specificata
a panelului

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Scrolling
<ScrollViewer>
<StackPanel>
<Button Content ="First" Background = "Green" Height ="40"/>
<Button Content ="Second" Background = "Red" Height ="40"/>
<Button Content ="Third" Background = "Pink" Height ="40"/>
<Button Content ="Fourth" Background = "Yellow" Height
="40"/>
<Button Content ="Fifth" Background = "Blue" Height ="40"/>
</StackPanel>
</ScrollViewer>

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Delegates
• Delegates
• conțin referinte la metode care au acelasi tip de return si acelasi tip de
parametrii ca si delegate-ul
• Evenimente
• cod care are nevoie sa fie informat cand apare un eveniment. Cel mai obisnuit
scenariu - GUI
• Când un apare un eveniment aplicatia trebuie sa stie ce metoda sa execute;
acest lucru se realizează prin transmiterea metodei care gestionează
evenimentul ca si parametru la un delegate.

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Declararea Delegates
• Clasa – definire proprietăți și metode apoi instanțiere obiecte din clasa
respectivă
• Delegates – declarare – precizam compilatorului ce fel de metoda va
referentia acel delegate apoi cream instante din acel delegate (in spate
compilatorul creaza clasa care reprezinta delegate-ul)

delegate void IntMethodInvoker(int x);

• Definim detalii despre semnătura si tipul de return al metodei pe care o


referențiază

delegate double TwoLongsOp(long first, long second);


delegate string GetAString();

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Utilizarea Delegates
private delegate string GetAString();
public static void Main()
{
int x = 40;
GetAString firstStringMethod = new GetAString(x.ToString);
// GetAString firstStringMethod = x.ToString;
Console.WriteLine($"String is {firstStringMethod()}");
// Console.WriteLine($"String is {x.ToString()}");
}
//firstStringMethod.Invoke();

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Action<T> and Func<T> Delegates
• Delegate-ul generic Action<T> - referentiaza o metoda cu tip de
return void ;
Action, Action<in T>, Action <in T1, in T2>… 16 param
• Delegate-ul Func<T> - referentiaza o metoda care are tip de return ;
Funct<out Result>, Func<in T, out Result>…16 param

delegate double DoubleOp(double x);

Func<double, double>
MEDII DE PROGRAMARE ȘI DEZVOLTARE
Bubble Sorter Class
bool swapped = true;
do
{swapped = false;
for (int i = 0; i < sortArray.Length—1; i++)
{
if (sortArray[i] > sortArray[i+1])) // problem with this test
{
int temp = sortArray[i];
sortArray[i] = sortArray[i + 1];
sortArray[i + 1] = temp;
swapped = true;
} } } while (swapped);

MEDII DE PROGRAMARE ȘI DEZVOLTARE


class BubbleSorter
{ static public void Sort<T>(IList<T> sortArray, Func<T, T, bool>
comparison)
{ bool swapped = true;
do
{ swapped = false;
for (int i = 0; i < sortArray.Count—1; i++)
{ if (comparison(sortArray[i+1], sortArray[i]))
{ T temp = sortArray[i];
sortArray[i] = sortArray[i + 1];
sortArray[i + 1] = temp;
swapped = true;
} } } while (swapped);
} }

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Multicast Delegate
• Un delegate poate sa referentieze mai multe metode
• Apeleaza succesiv fiecare metoda
• Return type void
Action<double> operations = MathOperations.MultiplyByTwo;
operations += MathOperations.Square;

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Evenimente
• Bazate pe Delegates pentru a oferi un mechanism de subscriere la
delegates

• Ex. Button – evenimentul Click

• O metoda handler (handler de evenimente) trebuie definita - este


invocata cand apare evenimentul Click

MEDII DE PROGRAMARE ȘI DEZVOLTARE


• 1. declararea delegate-ului
public delegate void EventHandler(object sender, EventArgs e);
• 2. declararea evenimentului gestionat prin delegate
• 3. scrierea metodei care să răspundă la eveniment (in clasa controlului care răspunde la
eveniment)
• 4. legarea evenimentului de handlerul său

MEDII DE PROGRAMARE ȘI DEZVOLTARE 20


Arhitectura de gestiune a evenimentelor
• Fiecare eveniment are
• un obiect (control) trimitător care aruncă (lansează) evenimentul
• un obiect (control) receptor care are metoda handler a
evenimentului
• Delegate: un obiect intermediar intre trimițător și receptor
• Este necesar pt ca trimițătorul nu știe ce procedură va gestiona
evenimentul
• Un delegate memorează o referință către metoda care va gestiona
evenimentul
• Definește numele handlerului si tipul argumentelor
• Sunt multicast -> pot memora referinte către mai multe handlere
• Functionează ca si un dispecer intre clasa care aruncă evenimentul prin
gestionarea unei liste de handlere de evenimente
MEDII DE PROGRAMARE ȘI DEZVOLTARE 21
Handlere de evenimente
• Metode legate de evenimente
• La aparitia evenimentului, se execută codul din handler

• Argumentele handlerului
• Un obiect care a aruncat evenimentul (sender)
• Un obiect care contine informatie specifica evenimentului (de tip
EventArgs)

MEDII DE PROGRAMARE ȘI DEZVOLTARE 22


Adaugarea / stergerea de handlere
• In designer:
• se selecteaza controlul dorit
• In fereastra de events, se selecteaza evenimentul care se doreste a fi prins

MEDII DE PROGRAMARE ȘI DEZVOLTARE 23


Expresii Lamdba

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Expresii Lambda
class Program
{static void Main()
{string mid = ", middle part,";
Func<string, string> lambda = param =>
{
param += mid;
param += " and this was added to the string.";
return param;
};
Console.WriteLine(lambda("Start of string"));
}}

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Expresii Lambda - Parametrii
Func<string, string> oneParam = s => $"change
uppercase {s.ToUpper()}";
Console.WriteLine(oneParam("test"));

Func<double, double, double> twoParams = (x, y)


=> x * y;
Console.WriteLine(twoParams(3, 2));

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Linii multiple de cod
Func<double, double> square = x => x * x;
Func<double, double> square = x =>
{
return x * x;
}
• Func<string, string> lambda = param =>
•{
• param += mid;
• param += " and this was added to the string.";
• return param;
• };

MEDII DE PROGRAMARE ȘI DEZVOLTARE


Curs 3
Evenimente Rutate
Comenzi WPF
Data Binding
Lect. dr. Florina Covaci
Eveniment WPF?
• Eveniment: un mesaj trimis de către un obiect la care
programul răspunde prin executarea unei bucăți de
cod

• Evenimentele pot fi lansate de către controale sau alte


obiecte ale aplicatiei
Tipuri de evenimente

•Directe – evenimentul este declansat si


gestionat de obiectul de origine
•Bubbling – de la obiectul de origine catre
nivelurile superioare din arborele de obiecte
•Tunneling – de la elementul parinte (ex.
Window) catre obiectul de origine
Evenimente rutate

• Rafinarea modelului standard de gestiune a evenimentelor astfel incat


acestea sa poata fi procesata intr-o maniera care sa corespunda
structurii arborescente a controalelor

• Modelul propaga automat un eveniment (sus/jos) intr-un arbore de


obiecte, executand handerul de eveniment corespunzator
Evenimente rutate
• un eveniment creat de către un control să apară la un alt control
dintre containerele in care stă controlul sursă

• Se permite astfel programatorului să decidă nivelul la care va prinde


evenimentul lansat in interfața grafică

• Se simplifică scrierea codului si se asigură consistență la tratarea


evenimentelor
Evenimente Bubbling
• Sunt aruncate de către controalele unde iși au originea și apoi de
către fiecare control container in care rezidă controlul de origine

• Evenimentele sunt prinse si tratate mai intâi la control și apoi la


containerul care il conține

• Permit unui grup complex de obiecte sa se comporte ca un singur


element
Evenimente de tip tunel

• Actioneaza invers ca si evenimentele bubbling


• Este aruncat mai intâi de către cel mai de sus container din arborele vizual si apoi in
jos de către fiecare container până la controlul de origine
• Permit ca evenimentele să fie prinse si tratate mai intâi la container si apoi la control
• Astfel de evenimente sunt marcate cu cuvantul Preview (PreviewKeyDown,
PreviewMouseDown)
• Evenimentele de tip tunel sunt definite in pereche cu cate un eveniment bubbling
• Mai intâi se aruncă evenimentul de tip tunel si apoi cel bubbling
• Argumentele sunt partajate intre aceste evenimente
RoutedEventArgs
• Fiecare eveniment include o instanță a clasei RoutedEventArgs (sau o clasă care mosteneste
RoutedEventArgs)
• Aceasta conține informație despre eveniment si sursa acestuia

Proprietăți ale RoutedEventArgs


• Handled: dacă evenimentul a fost tratat deja sau nu. Prin setarea la True se poate opri tratarea
ulterioară a evenimentelor de tip bubbling sau tunel
• OriginalSource: se obtine obiectul care a aruncat prima dată evenimentul (diferit de Source la
controale compozite)
• Source: obiectul care a aruncat evenimentul
• RoutedEvent: returnează evenimentul care a fost ridicat. Se foloseste atunci cand printr-un
handler se gestionează mai multe evenimente si dorim sa identificam care eveniment a fost
ridicat
Atașarea unui handler de evenimente
• Se poate face direct in codul XAML prin atasarea numelui metodei la eveniment
<Button Height="23" Margin="132,80,70,0" Name="button1"
VerticalAlignment="Top" Click="button1_Click">Button</Button>
• Evenimente attached: pentru un control se poate atasa un handler unui eveniment care
nu poate fi aruncat de către controlul respectiv
<Grid Button.Click="button_Click">
<Button Height="23" Margin="132,80,70,0" Name="button1"
VerticalAlignment="Top" >Button</Button>
</Grid>
• Codul de mai sus defineste handler pentru toate butoanele din grid
Tratarea unui eveniment de tip tunel sau bubbling

• Pentru a inhiba tratarea unui eveniment de tip tunel sau bubbling


la un anumit nivel in ierarhie, se foloseste proprietatea Handled
setata pe True

• Dacă se setează Handled la un eveniment de tip tunel, atunci se


consideră Handled si evenimentul bubbling pereche
Evenimente la nivel de aplicație
• Fiecare aplicație WPF este incapsulată intr-un obiect de tip Application care furnizează
un set de evenimente pentru gestiunea ciclului de viață al aplicație
• Evenimente la nivel de aplicație
• Activated: apare cand aplicația primeste focus de la un alt program (trecem de la o
alta aplicatie la programul nostru)
• Deactivated: cand aplicatia lasă focusul unui alt program
• DispatcherUnhandledException: apare cand o excepție nu este tratată in aplicație.
Se poate gestiona o exceptie negestionată prin setarea
DispatcherUnhandledExceptionEventArgs.Handled la True
• Exit: apare cand aplicația este inchisă
• SessionEnding: cand se inchide sesiunea Windows adică la logoff sau computer
shutdown
• Startup: cand aplicația este pornită
Crearea unui handler de eveniment la nivel de aplicație
1. in solution explorer, click dreapta pe App.xaml si se apelează view code
2. se crează o metodă pentru a gestiona evenimentul
void App_Startup(object sender, StartupEventArgs e)
{
// Handle the event here
}

3. In XAML, se adaugă handlerul de eveniment la declararea tagului Application


<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml" Startup="App_Startup">
Comenzi WPF
• Un eveniment este specific unei clase de baza si poate fi utilizat doar
pentru clase care extind aceasta clasa de baza – sunt cuplate cu
clasele in care sunt definite

• Comenzile WPF sunt asemanatoare evenimentelor dar sunt


independente de un control specific si pot fi aplicate mai multor tipuri
de controale
Comenzi - utilizare
• Permit mai multor surse sa invoce acceasi logica a comenzii
• Exemplu de comenzi: Copy, Cut, Paste – pot fi invocate utilizand
diferite actiuni ale utilizatorului daca aceste actiuni sunt
implementate folosind comenzi
• O aplicatie poate permite unui utilizator sa fac cut la obiectele
selectate : facand click pe un buton, alegand un item din meniu sau
folsind o combinatie de taste cum ar fi CTRL+X ->utilizand comezni
putem lega acceasi logica la mai multe tipuri de actiuni ale
utilizatorului
Comenzi-utilizare
• O comanda poate specifica daca acea actiune este disponibila
• Ex. Cut – are sens doar daca obiectul este selectat; daca utilizatorul
incearca sa faca cut la un obiect sau text fara a exista ceva selectat, nu
se va intampla nimic -> aplicatiile indica acest lucru prin dezactivarea
butoanelor/meniurilor
• O comanda indica daca este posibila o actiune prin metoda
CanExecute
Utilizare
• RoutedCommand predefinite
• Un control care are suport nativ pentru gestiunea comenzilor
• Un control care are suport nativ pentru invocarea comenzilor

• Ex. Paste – comanda predefinita in clasa ApplicationCommands


• Un TextBox are suport nativ pentru a gestiona comanda Paste
• MenuItem are suport nativ pentru invocarea comenzilor
Comenzi exemplu

<StackPanel x:Name="mainStackPanel"> pasteMenuItem.Command =
ApplicationCommands.Paste;
<Menu
x:Name="stackPanelMenu">
<MenuItem • pasteMenuItem.CommandTarget
x:Name="pasteMenuItem" = pasteTextBox;
Command="ApplicationCommands.Paste
" />
</Menu>
<TextBox x:Name="pasteTextBox"
/>
</StackPanel>
Comenzi - arhitectura
• Obiectul Command: actiunea care se executa

• Sursa comenzii: obiectul care invoca comanda

• Targetul comenzii: obiectul pe care se executa comanda

• CommandBinding: obiectul care mapeaza logica comenzii la comanda


Comenzi
• Taskuri de nivel inalt care sunt executate in aplicație
• Se pot asocia orice număr de obiecte UI sau inputuri cu o comandă
astfel incat să se lege comanda de un handler care este executat
atunci cand sunt activate controalele
• Comanda poate fi dezactivată, atunci elementul de interfată la care
comanda este asociat este si el dezactivat
Comenzi predefinite
• ApplicationCommands - Close, Copy, Cut, Delete, Find, Open, Paste, Save, SaveAs, Redo,
Undo

• ComponentCommands -MoveDown, MoveFocusBack, MoveLeft, MoveRight, ScrollToEnd,


ScrollToHome

• EditingCommands - AlignCenter, CorrectSpellingError, DecreaseFontSize, EnterLineBreak


• MediaCommands - FastForward, NextTrack, Play, Rewind
• NavigationCommands - BrowseBack, BrowseForward, Favorites, LastPage, NextPage, Zoom

• Fiecare din aceste clase expune o varietate de obiecte comanda statice pe care le
putem utiliza in aplicatii; trebuie sa cream binding-uri si handlere pentru aceste
comenzi pt a le putea utiliza in aplicatie
Implementarea unei comenzi
1. se decide comanda care se doreste a se utiliza
2. se asociază comanda cu un control sau cu un input
3. se crează metoda care gestionează comanda
4. se crează controlul CommandBinding care leagă obiectul Command
la handlerul de comandă si optional, la o metodă care gestionează
Command.CanExecute
5. se adaugă CommandBinding la collectia Commands a controlului sau
la controlul Window
Invocarea comenzii
• Prin asocierea ei cu un control folosind un input sau invocare cu un input

Asocierea comenzii cu un control


• Majoritatea controalelor implementează interfata ICommandSource -> se permite ca acestea să fie asociate
cu o comandă care este apelată automat cand este invocat controlul
<Button Command="ApplicationCommands.Find" ...>Button</Button>

Invocarea comenzii cu un input


• Se pot inregistra evenimente de mouse sau tastatură cu obiecte de tip Command care invoca comanda cand
aceste evenimente apar
ApplicationCommands.Find.InputGestures.Add(new
MouseGesture(MouseAction.LeftClick, ModifierKeys.Control));
ApplicationCommands.Find.InputGestures.Add(new
KeyGesture(Key.Q, ModifierKeys.Control));
Handlere de comenzi
• Pentru executarea unui cod atunci cand se invoca o comandă trebuie creat un CommandBinding care leagă
comanda de un handler
• Handler corect al unei comenzi:
private void myCommandHandler(object sender, ExecutedRoutedEventArgs e)
{
// Handle the command here
}
• ExecutedRoutedEventArgs este derivata din RoutedEventsArgs si expune proprietatea Command care
returnează obiectul Command al comenzii

Crearea CommandBinding
-asociaza o comanda cu un command handler
CommandBinding abinding = new CommandBinding();
abinding.Command = ApplicationCommands.Find;
abinding.Executed += new ExecutedRoutedEventHandler(myCommandHandler);
this.CommandBindings.Add(abinding);
Comenzi Bubbling
• Comenzile se rostogolesc către containerele din varful ierarhiei in care
rezidă controlul unde se invoca comanda
• Fiecare control are propria colecție CommandBinding
• Se poate opri rostogolirea procesarea comenzii prin setarea
Handled=true
private void myCommandHandler(object sender,
ExecutedRoutedEventArgs e)
{
// Handle the command here
e.Handled = true;
}
Dezactivarea comenzilor
• Comenzile care nu sunt asociate in CommandBinding sunt automat dezactivate
• Fiecare control care are in proprietatea Command asignat o comandă disables devine la randul lui dezactivat
• Pentru a dezactiva comenzi care sunt assignate in Command si avem setat si CommandBinding se utilizează
Command.CanExecute
• Este un eveniment care determină dacă o comandă se poate executa

1. se crează o metodă să gestioneze evenimentul CanExecute


bool canExecute;
void abinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = canExecute;
}
2. Se setează handlerul evenimentului CanExecute a obiectului CommandBinding către această metodă
abinding.CanExecute += new
CanExecuteRoutedEventHandler(abinding_CanExecute);
WPF Data-Binding
• Controalele sunt implicate deseori in operatiile de data-binding

• Data binding – conectarea proprietatilor controalelor cu date care se


modifica pe parcursul ciclului de viata al aplicatiei
• Bifarea unui CheckBox pe baza unei proprietati de tip Boolean a unui
anumit obiect
• Afisarea datelor din baza de date in obiecte de tip DataGrid
• Conecatare unui Label la un intreg care reprezinta numarul de fisiere
intr-un director that represents the number of files in a folder
Operatii data-binding
• Sursa – ex. Proprietatea de tip Boolean property sau o bd relationala

• Destinatie – proprietatea controlului UI care utilizeaza datele


(ex. Proprietatea unui CheckBox sau TextBox)
Tipuri de data-binding
• One-time : Bindingul se face de la sursa la desinatie si se
efectueaza o singura data cand se porneste aplicatia
• One-way : Bindingul se face de la sursa la desinatie. Este util
pentru datele read-only deoarece nu se pot modifica datele
din
• Two-way : Utilizatorul poate modifica datele prin interfata .
Bindingul se realizeaza in ambele directii
• One-way-to-source: daca se modifica destinatia, sursa se
modifica
Curs 4
Accesul la date cu ADO.NET
WPF Data Binding
Lect. dr. Florina Covaci
ADO.NET

Set de namespace-uri care permit


interactiunea cu sisteme de baze de
date relationale
Modele de access la date in ADO.NET
Lucrul in mod conectat
• Obiectele connection
gestioneaza conexiunea cu
serverul de date
• Obiectele Command sunt
wrappere pentru comenzi Sql
• DataAdapter contin obiecte de
tip Command
• SelectCommand aduce date in
DataReader – care poate fi
iterat
Lucrul in mod conectat
Obiecte Connection
SqlConnection connection = var cnStringBuilder = new
new SqlConnection() SqlConnectionStringBuilder
{
InitialCatalog = "AutoLot",
connection.ConnectionString = DataSource = @"(localdb)\mssqllocaldb",
@"Data Source= IntegratedSecurity = true
(localdb)\mssqllocaldb;Integr };
ated Security=true;Initial
Catalog=AutoLot"; SqlConnection connection = new
SqlConnection()
connection.Open();
connection.ConnectionString =
cnStringBuilder.ConnectionString;

connection.Open();
Obiecte Command
• Clasa SqlCommand derivata din DbCommand este o reprezentare
obiectuala unei interogari SQL sau procedura stocata

string sql = "Select * From Books";


SqlCommand myCommand = new SqlCommand(sql,
connection);

SqlCommand testCommand = new SqlCommand();


testCommand.Connection = connection;
testCommand.CommandText = sql;
Obiecte Command parametrizate I

•Parametrii SQL sunt obiecte


•Reduc numarul de erori typo (prin
proprietati puternic tipizate)
•Obiectele command pastreaza o colectie de
parametrii individuali
Obiecte Command parametrizate II
public void InsertBook(Book book)
{ string sql = "Insert Into Books(An, Titlu, Autor) Values (@An, @Titlu,
@Autor)";
SqlCommand command = new SqlCommand(sql, sqlConnection)
{ SqlParameter parameter = new SqlParameter
{ParameterName = "@Titlu",
Value = book.Titlu,
SqlDbType = SqlDbType.Char,
Size = 50 };

command.Parameters.Add(parameter);

command.ExecuteNonQuery();
}
Obiecte Data Reader
• Stream-uri de date readonly
• Statement-uri SELECT – e nevoie sa iteram volume mari de date
SqlDataReader myDataReader =
myCommand.ExecuteReader())
{
while (myDataReader.Read())
{
Console.WriteLine("Titlu:”+ myDataReader[„Titlu"] +
“Autor: “+ myDataReader[„Autor"]);
}
Executia command
• ExecuteNonQuery –nu returneaza output,doar nr. de inregistrari
afectate

• ExecuteScalar - returneaza valoarea din prima coloana a primului


rand din setul de date rezultat

• Execute Reader – returneaza un DataReader


ExecuteNonQuery() I
• Utilizat cu insert,update si delete
• Returneaza un int care reprezinta nr de inregistrari afectate

public void InsertBook(int an, string titlu, string


autor)
{
string sql = "Insert Into Books (An, Titlu, Autor)
Values ('{an}', '{titlu}', '{autor}')";
SqlCommand command = new SqlCommand(sql,
sqlConnection)
int r=command.ExecuteNonQuery();
}
ExecuteNonQuery() II
public class Book
{
public int An { get; set; }
public string Titlu { get; set; }
public string Autor { get; set; }
}

public void InsertBook(Book book)


{
string sql = "Insert Into Books (An, Titlu, Autor) Values " +
$"('{book.An}', '{book.Titlu}', '{book.Autor}')";
SqlCommand command = new SqlCommand(sql, sqlConnection)
int r=command.ExecuteNonQuery();
}
Execute Scalar
Un singur rezultat: nr inregistrari, data curenta
server
public void CountBooks()
{
string sql = "Select count(*) from Books";
SqlCommand command = connection.CreateCommand();
command.CommandText = sql;
object count = command.ExecuteScalar();
Console.WriteLine("counted {count} book records");
}
Execute Reader
public static void ReadBooks()
{ string sql = "Select * from Books";
SqlCommand command = connection.CreateCommand();
command.CommandText = sql;
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
int id = reader.GetInt32(0);
string bookTitle = reader.GetString(1);
string publisher = reader.GetString(2);
}}}
Executarea unei proceduri stocate
• Un bloc de cod SQL care poarta un nume si se afla intr-o baza de date.
Poate returna un set de randuri sau efectua op. de inserare,
update,stergere
• Se comporta ca o metoda, doar este stocata intr-o baza de date

public string LookAuthor(string titlu)


{
string numeAutor;
SqlCommand command = new SqlCommand("GetAuthor",
sqlConnection))
{
command.CommandType = CommandType.StoredProcedure;
SqlParameter param = new SqlParameter // Execute the stored proc.
{ParameterName = "@titlu", command.ExecuteScalar();
SqlDbType = SqlDbType.Char, // Return output param.
Value = titlu, numeAutor =
(string)command.Parameters["@titlu"
Direction = ParameterDirection.Input };
].Value;
command.Parameters.Add(param);
}
return numeAutor;
// Output param.
}
param = new SqlParameter
{
ParameterName = "@author",
SqlDbType = SqlDbType.Char,
Size = 30,
Direction = ParameterDirection.Output
};
command.Parameters.Add(param);
Tranzactii in ADO.NET I
• ACID

• atomicitate (totul sau nimic),


• consistenta (starea datelor inainte si dupa tranzactie trebuie sa fie
valida),
• izolare (tranzactiile care se executa concurent nu au acces la starea
celeilalte),
• durabilitate (sunt salvate si logate).
Tranzactii in ADO.NET II
public static void TransactionSample()
{ //...
try{
tx = sqlConnection.BeginTransaction();
cmdInsert.Transaction = tx;
cmdRemove.Transaction = tx;
cmdInsert.ExecuteNonQuery();
cmdRemove.ExecuteNonQuery();
tx.Commit();
}
catch (Exception ex)
{
tx.Rollback();
}}
Modelul deconectat de acces la date
• Access deconectat: in aplicație se mentine un obiect care reprezinta un cache
local de date (DataSet)
• Obiectele de tip DataAdapter folosite pentru a realiza comunicarea intre
obiectul DataSet (local) si serverul de date
DataSet
• Reprezintă o copie locală a bazei de date
• Furnizează un mediu relațional de programare, prezentând
o vedere relațională asupra datelor
• Stochează date similar cu stocarea datelor intr-o bază de
date relatională
• Este compus din obiecte de tip DataTable
• Proprietatea Tables: colecție cuprinzând tabelele din
DataSet
• Proprietatea Relation: colectie a relatiilor ce leaga tabelele
DataSet
• DataSet tipizat (typed):
• mediul de programare a construit clase pentru DataSet si pentru fiecare
tabelă a datasetului
• Tabelele pot fi accesate prin numeObiectDataSet.numeTabelă
• Datele din tabele pot fi accesare prin numeObiectDataTable.numeColoana
• DataSet netipizat (untyped):
• Se folosesc clasele standard (DataSet, DataTable, DataRow etc)
• Tabelele se accesează prin numeObiectDataSet.Tables[“NumeTabela”]
• Datele se accesează prin numeObiectDataTable.Rows[“numeColoană”]
DataTable
• DataTable: o tabelă a unui BD, conținând randuri
(DataRow), coloane (DataColumn) si constrângeri
• La un moment dat, un obiect de tip DataTable conține
o copie locală a datelor dintr-o tabelă din sursa de date
• proprietatea Rows: o colecție de obiecte de tip
DataRow
• Proprietatea Columns: o colecție de obiecte de tip
DataColumn
DataAdapter
• Folosit ca și o punte pentru a muta datele intre obiectul DataSet si sursa de
date
• Asigură operațiile de transfer de date între sursa de date și obiectul DataSet
Modificarea datelor din DataSet
• Obiectele DataTable pot contine date in 3 versiuni:
• Original, curent si proposed
• La incarcarea datelor in DataTable datele sunt in starea current
• Operatia BeginEdit: pune linia in mod edit iar datele modificate devin versiunea
Proposed;
• Operatia EndEdit: versiunea Current devine Original, versiunea Proposed devine
Current
• A 2-a operatie BeginEdit: versiunea Current devine Proposed
• La o nouă operatie EndEdit: versiunea Proposed devine Current
• Etc
Modificarea datelor in DataSet - exemplu
• Current-> DataRow editRow=
phoneNumbersDataSet.PhoneNumbers.Rows[lstPhones.SelectedIndex];
• Proposed-> editRow.BeginEdit();
editRow["Phonenum"] = txtPhoneNumber.Text.Trim();
editRow["Subscriber"] = txtSubscriber.Text.Trim();
editRow.EndEdit();
• Original->
tblPhoneNumbersAdapter.Update(phoneNumbersDataSet.PhoneNumbers
);
phoneNumbersDataSet.AcceptChanges();
Salvarea modificarilor
try{
• Metoda AcceptChanges: resetează starea unui DataRow editRow=
DataRow la unchanged phoneNumbersDataSet.PhoneNumbers.Rows[lstPhones.
SelectedIndex];
editRow.BeginEdit();
• AcceptChanges se apelează după un update a
datelor către serverul de date …
editRow.EndEdit();
• RejectChanges: face roll-back la datele din
momentul crearii sau la ultimul apel a lui tblPhoneNumbersAdapter.Update(phoneNumbersDataS
AcceptChanges -> se copiază inapoi datele din et.PhoneNumbers);
versiunea Original phoneNumbersDataSet.AcceptChanges();
}
catch (DataException ex)
{
phoneNumbersDataSet.RejectChanges();
}
Viziunea asupra unui DataSet
• Este o BD relatională in memorie – cached data
• Nu furnizează proprietăți tranzacționale: atomicitate, consistență, izolare
și durabilitate
• Contine colecții de DataTable si DataRelation
• DataTable contine chei unice si străine pentru integritatea datelor
• Se poate crea un DataSet in mod programatic sau prin furnizarea unei
scheme XML – .xsd
• DataSet editor: se permite crearea si editarea grafica a unui fisier xsd pe
baza caruia se crează clase DataSet tipizate
• In meniul Project , Add new item, se selectează DataSet
Data Binding
• Ofera aplicatiilor o modalitate consistenta de a interactiona cu date
diverse (obiecte .net, XML etc)

• DataBinding- procesul care stabileste conexiunea dintre UI si datele


pe care le afiseaza aceasta
• cand datele din sursa de date isi modifica valoarea elementele care sunt
legate la acele date se modifica automat
• cand se modifica datele in elementele de interfata, sursa de date se poate
modifica automat pentru e reflecta modificarile
Actualizare sursa date
• TwoWay si OneWayToSource asculta schimbarile din target property
si le propaga inapoi la sursa (actualizeaza sursa) – ex. Editarea unui
text din TextBox va modifica valoarea din sursa de date
Actualizare sursa date
• se actualizeaza in momentul editarii/la terminarea editarii?
• Proprietatea Binding.UpdateSourceTrigger
Valoarea lui UpdateSourceTrigger Cand se actualizeaza sursa Scenarii de utilizare pentru
TextBox
LostFocus (implicit Cand controlul de tip TextBox isi Un TextBox este asociat cu o
pentru TextBox.Text) pierde focusul logica de validare

PropertyChanged Pe masura ce scriem in TextBox. TextBox controls intr-un chat

Explicit Cand aplicatia Controale TextBox intr-un


apeleaza UpdateSource. formular editabil (sursa se
actualizeaza doar cand un
utilizator face click pe un buton).
Crearea unui binding
• Componente:
• Binding target
• Target property
• Binding source
• Path to source value

• Specificare binding source


<Window.DataContext> <Button Background="{Binding
<Binding Source={StaticResource
Source="{StaticResource myDataSource}, Path=ColorName}"
myDataSource}"/> Width="150" Height="30">
</Window.DataContext>
Specificarea path
<Button Background="{Binding Source={StaticResource
myDataSource}, Path=ColorName}" Width="150" Height="30">

• In scenarii in care dorim sa facem binding la intregul obiect


<ListBox ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="true"/>

ListBox mosteneste DataContext de la elementul parinte


Binding la colectii
• Un obiect binding source object poate fi tratat
• colectie de date grupate impreuna (ex. rezutatul unei interogari asupra bd) –
ListBox, ListView, TreeView

• Proprietatea
ItemsControl.ItemsSource
• Implicit pentru proprietatea
ItemsSource bindingul e OneWay
Collection views
• Dupa ce un ItemsControl este legat la o colectie de date->operatii de
sortare, filtrare sau grupare a datelor – utilizam collection views –
clase care implementeaza intefata ICollectionView

• Un collection view reprezinta un layer peste colectia binding source


care permite navigarea si afisarea fara a modifica binding source

• Fiecare colectie sursa poate avea view-uri multiple asociate (ex. O


colectie de obiecte de tip Task – putem afisa datele in moduri diferite:
sortate dupa prioritate sau grupate)
Curs 5
Accesul la date - Entity Framework
Lect. dr. Florina Covaci
Entity Framework - ADO.NET API
• Introdus incepand cu versiunea .NET 3.5
• EF Core – versiune multi-platforma a lui EF

• Permite interacțiunea cu datele din bazele de date relaționale


utilizând un model mapat cu functionalitatea de business din
aplicație.
• Un set de date - o colecție de rânduri și coloane => colecție obiecte
tipizate denumite entități
• Aceste entități pot fi interogate utilizând LINQ, motorul EF va traduce
LINQ în interogări SQL.
Rolul entitatilor
• Entitatile reprezinta un model conceptual al bazei de date - EDM
(Entity data model)

• EDM – un set de clase client-side care sunt mapate la o baza de date


pe baza conventiilor definite in EF si pe baza unor configurari
Componente EF
1. Clasa DbContext - utilizata pentru interogarea bazei de date si pentru a
grupa modificarile pentru a putea fi scrise in bloc
• GetValidationErrors –validează intrările si returnează o colectie
System.Data.Entity.Validation.DbEntityValidationResults
• SaveChanges – salvează in baza de date toate modificările făcute in context. Returneaza numarul
de enitati efectate
• Configuration – Ofera acces la proprietățile de configurare ale contextului
• Database – ofera un mechanism pentru creare/stergerea/verificarea bazei de date, execută
proceduri stocate si expune funcționalități legate de tranzacții
• Evenimente:
• ObjectMaterialized – se apeleaza când un nou obiect entitate este creat din baza de date ca
rezultat al unei interogări
• SavingChanges – se apeleaza când modificările se dalveaza în baza de date, dar înainte de a
deveni persistente
Componente EF
2. Clasa derivată din DBContext – se trimite la constructor numele
string-ului de conexiune pentru clasa context
public class AutoLotEntities : DbContext
{
public AutoLotEntities() : base("name=AutoLotConnection")
{
}
}
Componente EF
3. Entity Sets DbSet<T> - adaugarea de tabele in context
• public virtual DbSet<Customer> Customers { get; set; }
• public virtual DbSet<Inventory> Inventory { get; set; }
• public virtual DbSet<Order> Orders { get; set; }

Membrii DbSet<T>:
• Add – adaugarea unui obiect in colectie ; acestea vor fi marcate cu starea
Added si vor fi inserate in baza de date cand se apeleaza SaveChanges pentru
DBContext
• Create – creaza o noua insanță de tipul specificat
• Find – gasește un rand dupa cheia primară și returnează un obiect
reprezentând acel rând
• Remove – marcheaza un obiect pentru ștergere
DbSet<T> - Exemplu
using (AutoLotEntities context = new AutoLotEntities())
{
context.Cars.Add(new Car() { ColorOfCar = "Black",
MakeOfCar = „BMW",
YearOfCar = „2018" });
context.SaveChanges();
}

• AutoLotEntities este o clasa derivata din DbContext. Proprietatea Cars


ofera acces la DbSet<Car>
Componente EF
4. DbChangeTracker - realizează tracking-ul automat al stării oricărui obiect
DbSet<T> în cadrul unui DbContext
Stările entităților:
• Detached – obiectul există, dar nu se face încă tracking pe el; se afla în această
stare imediat ce a fost creat si înainte sa fie adăugat la obiectul context
• Unchanged –obiectul nu a fost modificat de când a fost atașat la context sau de
la ultimul apel a lui SaveChanges()
• Added – obiectul este nou și a fost adăugat la obiectul context, iar metoda
SaveChanges() nu a fost apelată.
• Deleted – obiectul a fost sters marcat pentru stergere
• Modified – una din proprietățile obiectului a fost modificată si metoda
SaveChanges() nu a fost apelată
Componente EF
5. Adnotări – reprezintă atribute utilizate pentru modelarea entităților in vederea
maparii cu bd
• Key – definește cheia primară. Nu este necesara daca proprietatea se numeste Id
sau combina numele clasei cu Id- ex.
• Required –proprietățile nu pot lua valori null
• ForeignKey –definește o proprietate care este utilizată ca și cheie străină
• NotMapped – o proprietate nu este mapată pe un câmp al bazei de date
• ConcurrencyCheck – marchează un camp pentru a fi verificat in cazuri de
concurență când se realizează inserări, actualizări sau ștergeri
• Table/ Column – permite numirea claselor si campurilor diferit față de numele din
baza de date. Atributul table permite specificarea inclusiv a numelui schemei
bazei de date
• DatabaseGenerated – specifică faptul că un câmp este generat din baza de date
cum ar fi Identity
Modelarea relatiilor intre entitati
• O relatie defineste modul in care doua entiati se raporteaza una la
cealaltata

• Intr-o baza de date relationala ->constrangere de cheie straina


Definire termeni

• Entitate dependenta : O entitate care contine proprietati de tip cheie


straina. Este referita uneori ca si entitatea copil din relatie
• Entiate principala: Entiatatea care contine proprietatea de tip cheie
primara. Este referita uneori ca si entitatea parinte din relatie
• Navigation property : O proprietate definita in entitatea principala
si/sau dependenta care referentiaza entitatea relationata
• Collection navigation property: Contine referinte la mai multe entiati
• Reference navigation property: Contine referinte la o singura entitate
Entitate principala-dependenta
public class Blog public class Post {
{ public int PostId { get; set; }
public int BlogId { get; set; } public string Title { get; set; }
public string Url { get; set; } public string Content { get; set; }
public List<Post> Posts { get; set; }
public int BlogId { get; set; }
}
public Blog Blog { get; set; }
}
Relatii definite complet
• Avem navigation properties definite la ambele capete ale relatiei si o proprietate de tip
cheie straina definite in entitatea dependenta
public class Blog {
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post {
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
No foreign key property
• Desi este recomandat sa definim o proprietate de tip cheie straina in
entiatea dependenta, nu este obligatoriu
• Daca nu este gasita o cheie straina se creaza automat o proprietate de
tip cheie straina shadow

• Proprietatile shadow – proprietati care nu sunt definite in clasa


entitate .NET dar sunt definite pentru acea entitate in modelul EF.
-valoarea si starea acelor entitati sunt gestionate
de ChangeTracker
No foreign key property
public class Blog
{ public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post {
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog { get; set; }
}
Configurare manuala prin Fluent API
class MyContext : DbContext {
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts); }
}
Cascade Delete
protected override void OnModelCreating(ModelBuilder
modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.OnDelete(DeleteBehavior.Cascade);
}
Relatie one-to-one
-au un reference navigation property la ambele capete ale relatiei
public class Blog public class BlogImage
{ { public int BlogImageId { get;
public int BlogId { get; set; } set; }
public string Url { get; set; } public byte[] Image { get; set;
}
public BlogImage BlogImage {
get; set; } public string Caption { get;
set; }
}

public int BlogId { get; set; }


public Blog Blog { get; set; } }
Configurare manuala cu Fluent API
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogImage> BlogImages { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
}
}
Relatie many-to-many
-necesita un collection navigation property la ambele capete (EF Core 5.0)
public class Post public class Tag
{ public int PostId { get; set; } {
public string Title { get; set; } public string TagId { get; set; }
public string Content { get; set; } public ICollection<Post> Posts {
public ICollection<Tag> Tags { get; set; get; set; }
} }
}
Relatie many-to-many
entitate de tip join
public class Post public class Tag
{ public int PostId { get; set; } { public string TagId { get; set; }
public List<PostTag> PostTags { get;
public string Title { get; set; } set; }
public string Content { get; set; } }
public List<PostTag> PostTags { get;
set; }
public class PostTag {
}
public DateTime PublicationDate {
get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Configurare manuala cu Fluent API
public class MyContext : DbContext
{ public MyContext(DbContextOptions<MyContext> options) : base(options) {
}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<PostTag>()
.HasKey(t => new { t.PostId, t.TagId });

modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);

modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId); } }
Operații - Adăugare • Metoda Add() method primeste ca si paramteru
un obiect de tip Car si il adauga la colectia Cars
• Se crează o instanță , se adauga la in clasa context AutoLotEntities
proprietatea DbSet<T>
corespunzătoare din clasa context • Cand un obiect este adăugat la un DbSet<T>,
și apoi se apeleaza SaveChanges() DbChangeTracker marchează starea noului
private static int AddNewRecord() obiect ca fiind EntityState.Added.
{
• La apelul SaveChanges , se generează interogări
using (var context = new
AutoLotEntities()) SQL pentru modificările aflate in așteptare
{
• Dacă există mai mult de o modificare, atunci
var car = new Car() { Make = „Opel",
Color = "Brown", Year=„2017"}; acestea se vor executa in tranzactie
context.Cars.Add(car);
• Daca nu apar erori, datele devin persistente in
context.SaveChanges();
return car.CarId; baza de date si proprietățile database-
} generated (în acest caz CarId) se actualizează cu
} valori din baza de date
Operații : Ștergere
• DbSet<T> • EntityState
private static void RemoveRecord(int carId) private static void RemoveRecordUsingEntityState(int carId)
{ {
using (var context = new AutoLotEntities()) using (var context = new AutoLotEntities())
{ {
Car carToDelete = context.Cars.Find(carId); Car carToDelete = new Car() { CarId = carId };
if (carToDelete != null) context.Entry(carToDelete).State = EntityState.Deleted;
{ try
context.Cars.Remove(carToDelete); {
if (context.Entry(carToDelete).State != EntityState.Deleted) context.SaveChanges();
{ }
throw new Exception("Unable to delete the record"); catch (DbUpdateConcurrencyException ex)
} {
context.SaveChanges(); WriteLine(ex);
Operații: Actualizare
private static void UpdateRecord(int carId)
{
using (var context = new AutoLotEntities())
{
Car carToUpdate = context.Cars.Find(carId);
if (carToUpdate != null)
{
WriteLine(context.Entry(carToUpdate).State);
carToUpdate.Color = "Blue";
WriteLine(context.Entry(carToUpdate).State);
context.SaveChanges();
SavingChanges
• Evenimentul se declansează dupa ce metoda SaveChanges() a fost
apelată, dar înainte ca baza de date să fie actualizată. Entitătile din
tranzactie sunt disponibile prin proprietatea ObjectStateEntry a lui
DbContext
• CurrentValues - valorile curente ale proprietăților entităților
• OriginalValues - valorile originale ale proprietăților entităților
• Entity – entitatea reprezentată de ObjectStateEntry object
• State – starea curentă a entității (Modified, Added, Deleted)
• AcceptChanges – acceptă valorile currente
• ChangeState – actualizeză starea unei unei entități
• GetModifiedProperties – returneaza numele tuturor proprietăților modificate
• RejectPropertyChanges – respinge modificarile
SavingChanges
• Permite respingerea modificărilor pentru un vehicul când acesta are
culoarea roșie
private void OnSavingChanges(object sender, EventArgs eventArgs)
{
var context = sender as ObjectContext;
if (context == null) return;
foreach (ObjectStateEntry item in
context.ObjectStateManager.GetObjectStateEntries(
EntityState.Modified | EntityState.Added))
{
if ((item.Entity as Inventory)!=null)
{
var entity = (Inventory) item.Entity;
if (entity.Color == "Red")
{
item.RejectPropertyChanges(nameof(entity.Color));
Incărcarea datelor
• Incărcarea datelor din baza de date în entități se poate realiza in trei
moduri:

• Lazy
– bazate pe setările de context

• Eager

• Explicit - controlat de dezvoltator


Incărcarea datelor - Lazy
Daca se face o cerere pentru o proprietate, EF creeaza un apel la baza de date, il executa si
populeaza obiectul cu detaliile. O interogare – se aduc toate masinile, si apoi pentru fiecare
masina o alta interogare cu care aduc comenzile
using (var context = new AutoLotEntities())
{
foreach (Car c in context.Cars)
{
foreach (Order o in c.Orders)
{
WriteLine(o.OrderId);
}
}
}
- Avantaj – se aduc doar datele de care e nevoie
- Dezavantaj - probleme de performanță în cazul în care nu este utilizat corespunzător
• Se poate dezactiva: context.Configuration.LazyLoadingEnabled = false;
Incărcarea datelor - Eager
• Cand e nevoie de incărcarea datelor relaționate pentru un obiect,
interogări multiple – ineficient; un admin de bd – join sql
• Eager loading
using (var context = new AutoLotEntities())
{
foreach (Car c in context.Cars.Include(c=>c.Orders))
{
foreach (Order o in c.Orders)
{
WriteLine(o.OrderId);
}
}
}
Incărcarea datelor - Explicit
• Incărcarea explicită folosind Collection()
foreach (Car c in context.Cars)
{
context.Entry(c).Collection(x => x.Orders).Load();
foreach (Order o in c.Orders)
{
WriteLine(o.OrderId);
}
}
Migrarea
• In procesul de dezvoltarea a unei aplicatii modelul se schimba
frecvent si nu mai este sincronizat cu baza de date
• La modificarea modelului-adaugare, stergere, modificare de entitati
stergem baza de date si EF creaza o noua baza de date
corespunzatoare modelului si apleaza Seed Data
• In productie avem date in baza de date -> nu putem sterge baza de
date
• EF Migration – actualizeaza baza de date
Add-Migration ExtendedModel
• EF genereaza cod care va creaza baza de date de la 0
• Directorul Migrations - fisier <timestamp>_ExtendedModel.cs

public partial class ExtendedModel : Migration


{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Book",
columns: table => new
{….
}
}
Remove-Migration ExtendedModel
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: “Books");

Metoda Down de apeleaza cand fac rollback la migrare


SnapShot pentru model
• Migrarea creaza un snapshot a schemei bazei de date curente

• Directorul Migrations/LibraryContextModelSnapshot.cs

• Cand creem o noua migrare, Ef determina ce s-a modificat comparand


modelul current cu modelul din snapshot
Curs 6
LINQ – Language Integrated Query
Lect. dr. Florina Covaci
LINQ

• Motivatie LINQ
• Aplicatiile vor accesa date la un anumit moment in timpul executiei
• Datele pot fi regasite in fisiere XML, bd relationale, colectii in memorie, siruri

• LINQ – introdus in .NET 3.5 – ofera un modalitate puternic tipizata de


a accesa date in diverse formate prin intermediul unui layer de
abstractizare
Structuri sintactice specifice
• LINQ poate fi inteles ca un limbaj de interogare puternic tipizat
incorporat in gramatica C#
• Putem construi expresii asememanatoare cu interogarile SQL, dar
interogarile SQL se pot aplica unor surse de date diferite inclusiv celor
care nu au nimic de a face cu bd relationale
• Desi interogarile LINQ sunt similar cu interogarile SQL sintaxa nu este
identica. De fapt multe interogari LINQ folosesc un format diferit de
cel utilizat la bd
• Nu incercam sa mapam sintaxa LINQ to SQL, ci mai degraba le privim
ca interogari care “prin coincidenta” seamana cu SQL
Mecanisme LINQ
• Cand LINQ a fost introdus in .NET, limbajul C# continea o serie de
mecanisme pe care se bazeaza tehnologia LINQ
• Astfel, limbajul C# utilizeza urmatoarele mecanisme care se afla la
baza LINQ:
• Variabile cu tip implicit
• Sintaxa de initializare a obiectelor/colectiilor
• Expresii Lambda
• Metode extinse
• Tipuri anomime
Variabile cu tip implicit
• Cuvantul cheie var permite declararea unei variabile locale fara a specifica explicit tipul variabilei
• Totusi, varibila este puternic tipizata, deoarece compilatorul va determina tipul de date corect
bazandu-se pe atribuirea valorii
static void DeclareImplicitVars()
{
// Implicitly typed local variables.
var myInt = 0;
var myBool = true;
var myString = "Time, marches on...";
// Print out the underlying type.
Console.WriteLine("myInt is a: {0}", myInt.GetType().Name);
Console.WriteLine("myBool is a: {0}", myBool.GetType().Name);
Console.WriteLine("myString is a: {0}", myString.GetType().Name);
}

• Acest feature este foarte util cand utilizam LINQ – multe interogari LINQ vor returna o secventa de
tipuri de date, care nu sunt cunoscute pana la momentul compilarii, deci nu vom putea declara tipul
unei variabile explicit
Sintaxa de initializare a obiectelor/colectiilor
• Sintaxa de intializare colectie pentru a umple o lista List<T> cu obiecte Rectangle, fiecare avand
doua obiecte Point care reprezinta o pozitie determinata de doua coordonate (x,y):
List<Rectangle> myListOfRects = new List<Rectangle>
{
new Rectangle {TopLeft = new Point { X = 10, Y = 10 },
BottomRight = new Point { X = 200, Y = 200}},
new Rectangle {TopLeft = new Point { X = 2, Y = 2 },
BottomRight = new Point { X = 100, Y = 100}},
new Rectangle {TopLeft = new Point { X = 5, Y = 5 },
BottomRight = new Point { X = 90, Y = 75}}
};
• Aceasta sintaxa combinata cu variabilele cu tip implicit ne permit declararea de tipuri anonime,
utile la crearea de proiectii LINQ
Expresii Lambda
• operator (=>) permite construirea unei expresii lambda, care poate fi
folosita in orice moment - se invoca o metoda care necesita un
delegate puternic tipizat ca si argument
• Expresiile lambda simplica modul de lucru in .NET, reducand numarul
de linii de cod care trebuie scrise
• ( ArgumentsToProcess ) => { StatementsToProcessThem }
Metode extinse
• Metode extinse permit adaugarea de functionalitati la clasele existente fara a folosi mostenirea, si permit
adaugarea de noi functionalitati la clase sealed
• La crearea unei metode extinse primul paramteru este calificat cu cuvantul cheie this marcand tipul care se
extinde.
• Metodele extinse trebuie definite in cadrul unei clase statice si trebuie declarate folosind cuvantul cheie
static
namespace MyExtensions
{
static class ObjectExtensions
{
// Define an extension method to System.Object.
public static void DisplayDefiningAssembly(this object obj)
{
Console.WriteLine("{0} lives here:\n\t->{1}\n", obj.GetType().Name,
Assembly.GetAssembly(obj.GetType()));
}}}
• Cand creem interogari LINQ, utilizam metode extinse definite in .NET.
Tipuri anonime
• Generarea definitiei unei clase la compilare prin specificarea unui set de perechi nume-valoare

• Pentru a defini un tip anonim declaram o variabila cu tip implicit si specificam datele folosind
sintaxa de initializare a obiectelor

// Make an anonymous type that is composed of another.


var purchaseItem = new {
TimeBought = DateTime.Now,
ItemBought = new {Color = "Red", Make = "Saab", CurrentSpeed = 55},
Price = 34.000};
• LINQ foloseste tipuri anonime cand dorim sa proiectam noi feluri de date “on the fly”
• O colectie de obiecte Persoana – dorim sa utilizam LINQ pentru a obtine info despre varsta si CNP
Folosing o proiectie LINQ, permitem compilatorului sa genereze un nou tip anonim care contine
informatia dorita
Termenii LINQ
• Utilizand LINQ putem crea expresii de interogare folosind limbajul C#
• LINQ pot fi folosite in mai multe cazuri iar termenii folositi difera in
functie de acest lucru:
• LINQ to Objects: utilizarea interogarilor LINQ la siruri si colectii
• LINQ to XML: utilizarea LINQ pentru a manipula si interoga documente XML
• LINQ to DataSet: utilizarea interogarilor LINQ la obiecte de tip ADO.NET
DataSet.
• LINQ to Entities: utilizarea interogarilor LINQ in cadrul ADO.NET Entity
Framework.
• Parallel LINQ (PLINQ): procesarea paralela a datelor returnate de o interogare
LINQ
Interogari
• Specifica informatiile care trebuie incarcate din sursa de date

• Optional specifica cum trebuie sortate/grupate

• Sunt stocate intr-o variabila de interogare si initializate cu o expresie


de interogare

var result = from matchingItem in container select matchingItem;

• Interogarile sunt separate de executia acestora


Sintaxa de baza
• Corectitudinea sintactica a unei interogarii LINQ este validata la
compilare => ordinea operatorilor este importanta
• Expresiile LINQ sunt construite utilizand operatorii from, in, select
• Template-ul general:
var result = from matchingItem in container select matchingItem;

• Identificatorul aflat dupa operatorul from reprezinta un item care


corespunde criteriilor introgarii LINQ – poate avea orice nume.
• Identificatorul aflat dupa operatorul in reprezinta containerul de date
in care se face cautarea (sir, colectie, document XML, etc.).
Selectarea itemilor din container
static void static void
SelectEverything(ProductInfo[] ListProductNames(ProductInfo[]
products) products)
{ {
Console.WriteLine("All product
details:"); Console.WriteLine("Only
var allProducts = from p in product names:");
products select p; var names = from p in products
foreach (var prod in select p.Name;
allProducts)
{ foreach (var n in names)
Console.WriteLine(prod.ToString {
()); Console.WriteLine("Name: {0}",
} n);
}
}}
Obtinerea de subseturi de date
• Template-ul general:
var result = from item in container where BooleanExpression select item;

static void GetOverstock(ProductInfo[] products)


{
Console.WriteLine("The overstock items!");
var overstock = from p in products where p.NumberInStock > 25
&&p.ExpDate=DateTime.Today.AddDays(7) select p;
foreach (ProductInfo c in overstock)
{
Console.WriteLine(c.ToString());
}
Proiectarea unor noi tipuri de date
static void • Cand interogarea LINQ creaza o
GetNamesAndDescriptions(ProductInfo[ proiectie nu va cunoaste tipul de
] products) date- e obligatorie utilizarea var
{ static var
GetProjectedSubset(ProductInfo[]
Console.WriteLine("Names and products)
Descriptions:");
{
var nameDesc = from p in products var nameDesc = from p in products
select new { p.Name, p.Description select new { p.Name, p.Description
}; // tip de date anonim };
foreach (var item in nameDesc) return nameDesc; // Nu!
}
{
• Nu putem scrie metode care
// Could also use Name and
Description properties directly. returneaza tipuri implicite
Console.WriteLine(item.ToString());
return nameDesc.ToArray();
}}
Numarul de itemi returnati
static void GetCountFromQuery()
{
string[] currentVideoGames = {"Morrowind", "Uncharted 2", "Fallout
3", "Daxter",
"System Shock 2"};
int numb = (from g in currentVideoGames where g.Length > 6 select
g).Count();
Console.WriteLine("{0} items honor the LINQ query.", numb);
}
• Count() - metoda extinsa a clasei Enumerable
Inversarea ordinii din setul obtinut
static void ReverseEverything(ProductInfo[] products)
{
Console.WriteLine("Product in reverse:");
var allProducts = from p in products select p;
foreach (var prod in allProducts.Reverse())
{
Console.WriteLine(prod.ToString());
}
}
• Reverse() - metoda extinsa a clasei Enumerable
Sortarea
static void AlphabetizeProductNames(ProductInfo[] products)
{
// produse in ordine alfabetica.
var subset = from p in products orderby p.Name select p;
Console.WriteLine("Ordered by Name:");
foreach (var p in subset)
{
Console.WriteLine(p.ToString());
}
}
• var subset = from p in products orderby p.Name ascending select p;
• var subset = from p in products orderby p.Name descending select p;
Gruparea
var queryCustomersByCity =
from cust in customers
group cust by cust.City;

foreach (var customerGroup in queryCustomersByCity)


{
Console.WriteLine(customerGroup.Key);
foreach (Customer customer in customerGroup)
{
Console.WriteLine(" {0}", customer.Name);
}
}
Join
Gasim clientii si distribuitori care se afla in acceasi locatie
var innerJoinQuery =
from cust in customers
join dist in distributors on cust.City equals dist.City
select new { CustomerName = cust.Name, DistributorName =
dist.Name };

-creeaza asocieri intre secvente care nu sunt explicit modelate in sursa


de date
-Clauza join se aplica colectiilor de obiecte nu direct tabelelor din bd
- Cheile straine din model – colectie de item-uri
- from order in Customer.Orders
Diferenta intre doua containere
static void DisplayDiff()
{
List<string> myCars = new List<String> {"Yugo", "Aztec", "BMW"};
List<string> yourCars = new List<String>{"BMW", "Saab", "Aztec" };
var carDiff = (from c in myCars select c).Except(from c2 in yourCars
select c2);
Console.WriteLine("Here is what you don't have, but I do:");
foreach (string s in carDiff)
Console.WriteLine(s);}

• Except() - metoda extinsa a clasei Enumerable


Intersectia a doua containere
static void DisplayIntersection()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec"
};
// Get the common members.
var carIntersect = (from c in myCars select c).Intersect(from c2 in
yourCars select c2);
Console.WriteLine("Here is what we have in common:");
foreach (string s in carIntersect)
Console.WriteLine(s);}
• Intersect() - metoda extinsa a clasei Enumerable
Reuniunea
static void DisplayUnion()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec"
};
// Get the union of these containers.
var carUnion = (from c in myCars select c).Union(from c2 in
yourCars select c2);
Console.WriteLine("Here is everything:");
foreach (string s in carUnion)
Console.WriteLine(s);
• Union() - metoda extinsa a clasei Enumerable
Concatenarea
static void DisplayConcat()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec" };
var carConcat = (from c in myCars select c).Concat(from c2 in yourCars
select c2);
foreach (string s in carConcat)
Console.WriteLine(s);
}

• Concat() - metoda extinsa a clasei Enumerable


Eliminarea duplicatelor
static void DisplayConcatNoDups()
{
List<string> myCars = new List<String> { "Yugo", "Aztec", "BMW" };
List<string> yourCars = new List<String> { "BMW", "Saab", "Aztec"
};
var carConcat = (from c in myCars select c).Concat(from c2 in
yourCars select c2);
foreach (string s in carConcat.Distinct())
Console.WriteLine(s);
}
• Distinct() - metoda extinsa a clasei Enumerable
Operatori de agregare LINQ
static void AggregateOps()
{
double[] winterTemps = { 2.0, -21.3, 8, -4, 0, 8.2 };
// Exemple de agregare
Console.WriteLine("Max temp: {0}", (from t in winterTemps select
t).Max());
Console.WriteLine("Min temp: {0}", (from t in winterTemps select
t).Min());
Console.WriteLine("Average temp: {0}", (from t in winterTemps
select t).Average());
Console.WriteLine("Sum of all temps: {0}", (from t in winterTemps
select t).Sum());
}
Cu LINQ vs. Fara LINQ
static void static void QueryOverStringsLongHand()
QueryOverStrings() {
{ string[] currentVideoGames = {"Morrowind",
string[] currentVideoGames = "Uncharted 2", "Fallout 3", "Daxter", "System
{"Morrowind", "Uncharted 2", Shock 2"};
"Fallout 3", "Daxter", string[] gamesWithSpaces = new string[5];
"System Shock 2"}; for (int i = 0; i < currentVideoGames.Length;
i++)
var subset = from g in
currentVideoGames where { if (currentVideoGames[i].Contains(" "))
g.Contains(" ") orderby g gamesWithSpaces[i] = currentVideoGames[i];
select g; }
foreach (string s in subset) Array.Sort(gamesWithSpaces);
Console.WriteLine("Item: foreach (string s in gamesWithSpaces)
{0}", s);
{ if( s != null)
}
Console.WriteLine("Item: {0}", s);
}}
Relatii intre tipurile interogarilor LINQ
Relatii intre tipurile interogarilor LINQ
Relatii intre tipurile interogarilor LINQ
Relatii intre tipurile interogarilor LINQ
Executia amanata
• Interogarile LINQ nu sunt evaluate decat in momentul in care se itereaza secventa
• Beneficiu – putem utiliza o interogare LINQ de mai multe ori pentru acelasi container si putem fi siguri ca vom obtine
rezultate actualizate

static void QueryOverInts()


{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// Get numbers less than ten.
var subset = from i in numbers where i < 10 select i;
// LINQ se evalueaza aici!
foreach (var i in subset)
Console.WriteLine("{0} < 10", i);
Console.WriteLine();
// Modificam datele
numbers[0] = 4;
// Se reevalueaza!
foreach (var j in subset)
Console.WriteLine("{0} < 10", j);
Console.WriteLine();
}
Executia Imediata
• Cand e nevoie sa evaluam o expresie LINQ fara a itera colectia putem utiliza
metode extinse ale clasei Enumerable ToArray<T>(),
ToDictionary<TSource,TKey>(), and ToList<T>().
• Aceste metode vor face ca interogarea LINQ sa se execute in momentul apelarii
metodei pentru a obtine datele
static void ImmediateExecution()
{
int[] numbers = { 10, 20, 30, 40, 1, 2, 3, 8 };
// obtinem datele IMEDIAT ca int[].
int[] subsetAsIntArray = (from i in numbers where i < 10 select i).ToArray<int>();
// obtinem datele IMEDIAT ca List<int>.
List<int> subsetAsListOfInts = (from i in numbers where i < 10 select i).ToList<int>();
}
Interogari cu agregari
var subset = from i in numbers where i < 10
select i;

int NumCount = subset.Count();

Interogari care realizeaza agregari se executa fara o instructiune foreach


pentru ca interogarea in sine foloseste un foreach pentru a returna
rezultatul
Query Syntax vs. Method Syntax
int[] numbers = { 5, 10, 8, foreach (int i in numQuery1)
3, 6, 12}; { Console.Write(i + " "); }
//Query syntax:
IEnumerable<int> numQuery1 = from Console.WriteLine(System.Envi
num in numbers ronment.NewLine);
where num % 2 == 0
orderby num select num;
foreach (int i in numQuery2)
//Method syntax: { Console.Write(i + " "); }
IEnumerable<int> numQuery2 =
numbers.Where(num => num % 2 ==
0).OrderBy(n => n);
Curs 7
Aplicatii Web – ASP.NET Core
Lect. dr. Florina Covaci
ASP.NET Core
• Necesitatea ca ASP.NET si Entity Framework sa ruleze pe alte SO in
afara de Windows
• Unificarea modului de dezvoltare a web app si web API
• Arhitectura care usureaza testarea
• Posibilitatea de a dezvolta si rula aplicatii pe Windows, macOS, and
Linux.
• Open-source si centrat pe comunitate
• Integrarea de framework-uri client-side – Blazor –creare de UIs
utilizand C# in loc de JavaScript.
• Suporta instalarea de versiuni diferite ale .Net Core – side by side
versioning
Tehnologii Web
• HTML

• CSS

• JavaScript si TypeScript

• Librarii de scripting
HTML si CSS
• Cascade Style Sheets - definesc felul in care arata pagina web
• HTML tagul pentru list item <li> definea modul in care este vizualizat-
cerc,disc,patrat ->CSS
• Cu CSS se pot utiliza selectori flexibili pentru elemente HTML si se pot
defini stiluri pentru aceste elemente. Putem selecta un element prin
ID sau nume si se pot defini clase CSS care pot fi referentiate din cod
HTML
• Se pot utiliza template-uri Bootstrap - o colectie de conventii CSS si
HTML care pot fi adaptate facil
• www.getbootstrap.com - documentatie si template-uri
JavaScript si TypeScript
• Putem modifica dynamic elemente client-side
• ECMAScript – standard care defineste functionalitatile
curente si vitoare pentru JavaScript.
• Implementarea Microsoft pentru JavaScript se numeste
JScript.
• TypeScript similar cu JavaScript. Sintaxa TypeScript se
bazeaza pe JavaScript, dar aduce noi functionalitati (ex.
Cod puternic tipizat, adnotari)
• Exista multe similaritati intre C# si TypeScript
• Documentatie la : www.typescriptlang.org.
Librarii de scripting
• Pot fi utilizate server-side impreuna cu ASP.NET Core
• jQuery (http://www.jquery.org) –utilizata pentru a gestiona unitar modul in
care diferite browsere gestioneaza evenimentele
• Angular (https://angular.io) librarie de la Google bazata pe patternul MVC
creata pentru a simplifica dezvoltarea si testarea aplicatiilor web de tip
single-page (Spre deosebire de ASP.NET MVC, Angular ofera patternul MVC
in codul client-side)
• React (https://reactjs.org) librarie de la FaceBook care ofera functionalitati
prin care se pot actualiza facil intefetele utilizator pe masura ce datele se
modifica in background
• Visual Studio ofera template-uri pentru Angular si React
Structura unei aplicatii Web – cu Razor
• In directorul wwwroot regasim continut de tip client-side CSS, JavaScript,
imagini, si orice alt continut non-programatic

• Directorul Pages contine pagini Razor si fisiere suport. Fiecare pagina este o
pereche a urmatoarelor fisiere:
• Un fisier .cshtml care contine markup HTML si cod C# utilizand sintaxa Razor.
• Un fisier .cshtml.cs care contine cod C# pentru a gestiona evenimentele la nivel de
pagina.

• Fisierele suport au nume care incepe cu “_”. Ex fisierul “_Layout.cshtml”


configureaza elemente UI comune tuturor. Ex. acest fisier seteaza
navigation menu in partea de sus a paginii si informatia despre copyright in
partea de jos a paginii.
Structura unei aplicatii Web – cu Razor
• appSettings.json
• Contine date de configurare, precum string-uri de conexiune

• Program.cs
Punctul de start al aplicatiei.

• Startup.cs
• Contine cod care configureaza comportamentul aplicatiei
Clasa Program
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
La pornire aplicatia ASP.NET Core creaza un obiect host
Incarca configurarile din fisierul appsettings.json
Clasa Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
• Cu metoda ConfigureServices se configureaza, in containerul dependency injection ,
servicii necesare aplicatiei. IServiceCollection contine toate serviciile deja inregistrate si
permite adaugarea de noi servicii
• Cu metoda Configure este definit modul de gestionare a cererilor ca o serie de
componente middleware
Configurare Servicii

• Serviciu – o componenta reutilizabila care furnizeaza functionalitate


aplicatiei

• Serviciile se inregistreaza in metoda ConfigureServices si sunt


consumate in aplicatie prin dependency injection
Dependency Injection
• Este un design pattern care ofera o cuplare slaba

• Un serviciu este definit doar cu o interfata , tipul concret care


implementeaza interfata este injectat

• Cu mecanismul ASP.NET Core dependency injection, injectarea se face


cu constructori cu argumente de tip interfata
Middleware
• Reprezinta o componenta software care este integrata in pipeline-ul
aplicatiei - gestioneaza cereri
• Fiecare componenta decide daca trebuie sa trimita mai departe
cererea la urmatoarea componenta din pipeline
• Fiecare componenta din pipeline este responsabila pentru a invoca
urmatoarea componenta sau pentru a scurt-circuita pipeline-ul
(impiedica componentele urmatoare sa proceseze cererea)
• Componenta aferenta gestiunii exceptiilor trebuie sa fie apelata
devreme in pipeline astfel incat acestea sa nu apara in componentele
urmatoare din pipeline
Creare de aplicatii web cu Razor
• Razor este o sintaxa de tip markup pentru a incorpora cod de tip
server-side in pagini web
• Sintaxa Razor consta in Razor markup, C#, si HTML
• Limbajul Razor implicit este HTML. Interpretarea HTML din markup-ul
Razor este similara cu interpretarea HTML dintr-un fisier HTML.
Pagini Razor
• Paginile Razor sunt derivate din PageModel
Index.cshtml
public class IndexModel : PageModel
{
}
Index.cshtml.cs
Razor poate trece de la HTML la C# sau la mark-up specific Razor
Cand simbolul @ este urmat de un cuvant cheie Razor trecerea este
facuta la mark-up specific Razor, altfel la C#
Razor evalueaza expresiile C# si le interpreteaza in output HTML.
Directive Razor

• @page - permite ca pagina sa poata gestiona cereri


- trebuie sa fie prima directiva care apare in fisier

• @model – specifica modelul trasmis catre pagina Razor


-reprezentat de clasa derivata din PageModel
Pagina Layout
• Pages/Shared/_Layout.cshtml
• Furnizeaza un Layout unitar pentru intreaga aplicatie
• Permite containerului HTML pentru layout
• Sa fie specificat intr-un singur loc
• Sa fie utilizat in pagini multiple

• @RenderBody ()- placeholder care permite afisare


continutului specific pentru fiecare pagina
ViewData si Layout
@{
ViewData["Title"] = "Home page";
}

• Exemplu de trecere in C#
• {} delimiteaza un bloc de cod C#
• Clasa de baza Pagemodel contine o proprietate de tip dictionar, care
este utilizata pentru a trimite date la un View
• Obiectele sunt adaugate folosind perechi de tipul cheie/valoare
• Proprietatea Title este adaugata la dictionarul ViewData
ViewData si Layout
• Proprietatea title este folosita in Pages/Shared/_Layout.cshtml
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-
width, initial-scale=1.0" />
<title>@ViewData["Title"] – Sample App</title>
@*Comentariu*@ spre deosebire de <!-- -->
Setarea Layout-ului
• Se realizeaza in Pages/_ViewStart.cshtml
@{
Layout = "_Layout";
}
• Seteaza pentru toate paginile din directorul Pages, layout-ul definit in
Pages/Shared/_Layout.cshtml
Tag Helpers
• Tag Helpers permit codului de tip server-side sa participe la crearea si
afisarea elementelor HTML in fisiere Razor
• Tag Helpers folosesc limbaj C# si targeteaza elemente HTML bazandu-
se pe numele elementului si numele atributului
• Sunt prefixati cu “asp-”
Ex. Label Tag Helper targeteaza elementul HTML <label> cand
sunt aplicate atributele tag helperului
<label asp-for="Title"></label>
Tag Helpers
public class Book
{ • Genereaza eticheta si atributul for pentru
public int ID { get; set; } un element de tip label
[Display(Name = "Book Title")]
• Tipizare puternica cu proprietatea
public string Title { get; set; }
modelului
public string Author { get; set; }
}
• A generat valoarea pentru atributul “Title”,
Markup Razor care reprezinta id-ul asociat cu elementul
@model Book <input>
<label asp-for="Title"></label> • Tag-Helper-ul genereaza elemente id si for
<input asp-for="Title" /> consistente, astfel incat acestea sa fie
Se genereaza HTML
corect asociate
• Eticheta se genereaza din atributul Display
<label for="Title">Book Title</label>
Ce ofera Tag Helpers?
1. Experienta de dezvoltare asemanatoare HTML – de cele mai multe
ori, markup-ul Razor cu tag helpers este asemanator cu cel standard
HTML. Designeri front-end familiarizati cu HTML/CSS/JavaScript pot
edita markup Razor fara a invata sintaxa Razor

2. Suport IntelliSense pentru crearea de markup HTML si Razor –


productivitate mai buna cand se utilizeaza tag helpers decat
scrierea de markup C# Razor
Ce ofera Tag Helpers?
3. Cod mai robust si mai facil mentabil utilizand doar informatia de la
server
Ex. Uzual la actualizarea imaginilor se modifica numele imaginii si
fiecare referinta la imagine necesita sa fie actualizata. Imaginile se
salveaza in cache din motive de performanta, iar daca nu se schimba
numele imaginii exista riscul ca la client sa se afiseze o copie a imaginii
din cache
• Image Tag helper – adauga un numar de versiune la numele imaginii,
astfel ca la modificarea imaginii, serverul genereaza automat o noua
versiune pentru imagine; se garanteaza afisarea imaginii curente
Image Tag Helper
• Image Tag Helper adauga tag-ului <img> elemente privind
comportamentul cache pentru fisiere statice de tip imagine.
• Un string cu o valoare unica de hash este adaugata la URL permite
reincarcarea imaginii de la server si nu din cache-ul clientului
• Daca se modifica imaginea de pe server, un URL unic este generat
care include string-ul actualizat

<img src="~/images/asplogo.png" asp-append-version="true">


Se genereaza HTML
<img src="/images/asplogo.png?v=Kl_dqr9NVtnMdsM2MUg4qthUnWZm5T1fCEimBPWDNgM">
Directiva @addTagHelper
• La crearea unei aplicatii web ASP.NET Core in fisierul _ViewImports.cshtml se
adauga directiva
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
• efect - Tag Helpers devin disponibili in cadrul paginilor.
• Fisierul _ViewImports.cshtml este mostenit de toate paginile
• Sintaxa wildcard ("*") specifica ca toate Tag Helpers din assembly-ul
(Microsoft.AspNetCore.Mvc.TagHelpers) vor fi disponibile pentru fiecare pagina
• Primul parametru @addTagHelper specifica care tag helpers sa fie disponibili, al
doilea parametru "Microsoft.AspNetCore.Mvc.TagHelpers" specifica numele
assembly-ului in care se gasesc Tag Helpers.
• Microsoft.AspNetCore.Mvc.TagHelpers este assembly-ul pentru tag helpers
predefiniti
• Se pot creea tag helpers custom
Anchor Tag Helper
• Adauga noi atribute tag-ului standard HTML (<a ... ></a>) pentru a
seta atributul href la o pagina specifica

<a asp-page="./Index">Back to List</a>


<a asp-page="./Edit" asp-route-id=“10">Edit</a>

Se genereaza HTML
<a href="/Index">Back to List</a>
<a href="/Edit? id=10">Edit</a>
Tag Helper pentru validare
• Validation Message Tag Helper
• afiseaza un mesaj de validare pentru proprietatea aferenta din modelul nostru
• este utilizat cu atributul asp-validation-for a unui element HTML <span>
• in general il utilizam dupa un tag helper input pentru acceasi proprietate, pentru a
afisa erorile de validare langa input-ul care a cauzat eroarea
<input asp-for="Book.Author" />
<span asp-validation-for="Book.Author"></span>
• Validation Summary Tag Helper afiseaza un rezumat al erorilor de validare
<div asp-validation-summary="ModelOnly"></div>
• Validarea se realizeaza pe baza constrangerilor si atributelor adnotarilor in
clasele din model
Tag Helpers custom
• Tag helper pentru tag-ul <email>Support</email>
• <a href="mailto:support@ubbcluj.ro">support@ubbcluj.ro</a>
using Microsoft.AspNetCore.Razor.TagHelpers;
public class EmailTagHelper : TagHelper
{ public override void Process(TagHelperContext
context, TagHelperOutput output)
{ output.TagName = "a"; // Inlocuieste <email> cu <a>
tag }
}
Tag Helpers custom
• Tag helpers utilizeaza o conventie de nume referitor la elementul pe
care il targeteaza
• Elementul targetat este radacina EmailTagHelper – email -> element
targetat <email>
• clasa EmailTagHelper mosteneste TagHelper. Clasa TagHelper ofera
metodele si proprietatile necesare pentru a crea tag helpers
• metoda suprascrisa Process controleaza ceea ce face tag helper-ul
cand este executat
Utilizare
• Pentru a fi vizibil in paginile Razor utilizam directiva @addTagHelper

• In _ViewImports.cshtml

@addTagHelper namespace.TagHelpers.EmailTagHelper, numeassembly


SetAttribute si SetContent
• Creem o ancora valida pentru email
public class EmailTagHelper : TagHelper
{
private const string EmailDomain = "ubbcluj.ro";

public string MailTo { get; set; }


public override void Process(TagHelperContext context,
TagHelperOutput output)
{
output.TagName = "a"; // inlocuieste <email> cu tag-ul <a>
var address = MailTo + "@" + EmailDomain;
output.Attributes.SetAttribute("href", "mailto:" + address);
output.Content.SetContent(address);
}
<email mail-to="Support"></email>
Curs 8
Sintaxa Razor
Gestiunea starii
Lect. dr. Florina Covaci
Sintaxa Razor
• Sitaxa de tip markup - incorporeaza code de tip server in paginile web
• Limbajul Razor implicit este HTML – ramane neschimbat la randare
• Pentru a trece la limbaj C# - simbolul @ - Razor evalueaza expresiile
C# si le randeaza in output HTML
• Cand simbolul @ este urmat de un cuvant cheie Razor – tranzitia se
face la markup specific Razor
• Atributele si continutul HTML care contin simbolul @ - nu este
interpretat ca un simbol de tranzitie
<a href="mailto:Support@contoso.com">Support@contoso.com</a>
Expresii implicite/explicite Razor
• Expresii implicite nu pot contine spatii
<p>@DateTime.Now</p>
<p>Last week: @DateTime.Now - TimeSpan.FromDays(7)</p>
Se randeaza
<p>Last week: 7/7/2016 4:39:52 PM - TimeSpan.FromDays(7)</p>
• Expresii explicite ()
<p>Last week this time: @(DateTime.Now - TimeSpan. FromDays(7)) </p>
Continutul @() este evaluat si randat
Expresii explicite
• Expresiile explicite pot fi utilizate pentru a concatena text cu un
rezultat al unei expresii
@
{
var joe = new Person("Joe", 33);
}

<p>Age@(joe.Age)</p> se randeaza <p>Age33</p>

<p>Age@joe.Age</p> e interpretata ca o adresa de email , se randeaza


<p>Age@joe.Age</p>
Blocuri de cod Razor
• Incep cu simbolul @ si sunt marcate de {}
• Spre deosebire de expresii, codul C# din interiorul blocurilor de cod nu este randat
• Blocurile de cod si expresiile intr-un view sunt definite in ordine

@{
var quote = " An apple a day keeps the doctor away ";
}
<p>@quote</p>
@{
quote = " The future depends on what you do today ";
}
<p>@quote</p>
Se randeaza
<p> An apple a day keeps the doctor away </p>
<p> The future depends on what you do today </p>
Functii locale in blocuri de cod
• Se pot delcara functii locale cu markup pentru a fi utilizate ca metode
template
@{
void RenderName(string name)
{ <p>Name: <strong>@name</strong></p>
}
RenderName(“John Keller");
RenderName("Martin Johnson");
}
<p>Name: <strong>John Keller</strong></p>
<p>Name: <strong>Martin Johnson</strong></p>
Tranzitie implicita
• Limbajul implicit intr-un bloc de cod este C#, dar poate face implicit
tranzitia la HTML

@{
var inCSharp = true;
<p>Now in HTML, was in C# @inCSharp</p>
}
Tranzitie explicita
• Delimitata - Pentru a defini o sectiune dintr-un bloc de cod care trebuie sa
randeze HTML adaugam tag-ul <text>

@for (var i = 0; i < people.Length; i++)


{ var person = people[i];
<text>Name: @person.Name</text>
}
• Inline - @:
@for (var i = 0; i < people.Length; i++)
{
var person = people[i];
@:Name: @person.Name
}
Structuri de control
• Conditionale
@if (value % 2 == 0)
{ <p>The value was even.</p> }
else
if (value >= 1337)
{ <p>The value is large.</p> }
else
{ <p>The value is odd and small.</p> }
Structuri de control - ciclice
@{ @foreach (var person in people)
var people = new Person[] { <p>Name: @person.Name</p>
{ new Person("Weston", 33), <p>Age: @person.Age</p>
new Person("Johnathon", 41), }
... };
}
@for (var i = 0; i < people.Length;
i++)
{ var person = people[i];
<p>Name: @person.Name</p>
<p>Age: @person.Age</p>
}
PageModel
• Prin conventie numele clasei este denumit dupa sablonul
<NumePagina>Model
• PageModel permite separarea logicii paginii fata de prezentarea
acesteia
• Metode handler:
• OnPost – ruleaza cand exista cereri POST (cand utilizatorul trimite un
formular)
• OnGet – initializeaza starea paginii
• Se pot adauga metode handler pentru orice verb HTTP
• Sufixul Async este optional dar este folosit adeseori pentru
functionare asincrona
Fluxul OnPostAsync
public async Task<IActionResult> • Se verifca erorile de validare
OnPostAsync()
{
• Daca nu exista erori se salveaza datele si se
if (!ModelState.IsValid) redirecteaza pagina
{
return Page();
• Daca exista erori se reafiseaza pagina cu
} mesaje de validare

_context.Book.Add(Book); • Erorile de validare trebuie detectate la client


await
_context.SaveChangesAsync();

return
RedirectToPage("./Index");
}
Binding
[BindProperty]
public Book Book { get; set; }

Legarea proprietatilor – nu mai este necesara conversia datelor HTTP


catre tipul modelului

<form method="post">
<input asp-for="Book.Title" />
</form>
Se face binding cu proprietatea Title
Antiforgery token
• <form method="post">
• <!-- Input and Submit elements -->
• <input name="__RequestVerificationToken" type="hidden"
value="<Antiforgery token >" />
• </form>
• FormTagHelper injecteaza token-uri antiforgery in elementul HTML
form
Atacuri Cross-Site Request Forgery
• Cunoscute si ca atacuri XSRF sau CSRF
• Atacuri asupra aplicatiilor web in care o aplicatie malitioasa poate
influenta interactiunea dintre un client browser si a aplicatie web care
a autentificat anterior acel client
• Posibile pentru ca browserele web trimit token-uri de autentificare
automat cu fiecare cerere catre un website
• Atac One-click sau session riding – profita de sesiunea autenficata
anterior
Exemplu
• Un utilizator se logheaza in www.good-banking-site.com, serverul
autentifica userul si trimite un raspuns care include un cookie de
autentificare
• Site-ul este vulnerabil pentru ca are incredere in cereri care vin
impreuna cu cookie-ul de autentificare
• Utilizatorul viziteaza un site Site-malitios www.bad-banking-site.com
contine un formular
Exemplu
<h1>Congratulations! You're a Winner!</h1>
<form action="http://good-banking-site.com/api/account"
method="post"> <input type="hidden" name="Transaction"
value="withdraw">
<input type="hidden" name="Amount" value="1000000">
<input type="submit" value="Click to collect your prize!">
</form>
-se trimite formularul-browserul face cererea si include automat
cookie-ul de autentificare, iar cerea va rula pe good-banking-site.com si
va putea executa orice operatie pe care o executa un utilizator
autentificat
Gestiunea starii in ASP.NET Core
• HTTP este un protocol stateless.

• Fara a intreprinde anumite actiuni, cererile HTTP sunt mesaje


independente

• Nu retin valorile primite de la utilizator si nici starea aplicatiei intre


cereri succesive
Cookie-urile (I)
• Cantitate mică de date scrisă la client si care este transmisă cu o
cerere la un website
• Cookiurile persistente se scriu ca si fisiere text pe masina client
• Ele rezistă si după ce browserul este inchis
Cookie-urile (II)
• Utilizare: identificarea unui utilizator intre mai multe vizite a unor
pagini web din website
personalizare, atunci cand contintul este customizat pentru
un utilizator cunoscut.

• In cele mai multe cazuri userul este doar identificat, nu autentificat

• Poate pastra username-ul sau un id, apoi se acceseaza setarile


personalizate cum ar fi culoarea preferata pentru background
Cookie-urile (II)
• Deoarece acestea se trimit impreuna cu fiecare cerere, marimea lor ar
trebui pastrata la minim – ex. Un identificator si valoarea acestuia
• Cele mai multe browsere restrictioneaza marimea cookie-urilor la
4096 bytes
• Deoarece cookie-urile pot fi modificate, acestea trebuie validate in
cadrul aplicatiei
• Acelasi utilizator poate accesa un website de la calculatoare diferite
• Pot fi sterse de utilizator sau pot expira, dar sunt cea mai durabila
forma de a persista date la client
Creare si utilizare cookie
• Creare cookie
Response.Cookies.Append("MyCookie", "value1");
• Citire cookie
var cookieValue = Request.Cookies["MyCookie"];
• Configurari aditionale - setam un timp de expirare
pentru a face cookie-ul persistent
var cookieOptions = new CookieOptions
{
Expires = DateTime.Now.AddDays(30)
};
Response.Cookies.Append("MyCookie", "value1", cookieOptions);
Sesiuni (I)
• Asigura persistenta datelor cat timp utilizatorul navigheaza in cadrul
unei aplicatii web
• Sunt considerate “efemere”- aplicatia ar trebui sa poata functiona si
fara datele de sesiune
• Datele critice trebuie salvate in bd si utilizate in sesiune doar pentru
optimizarea performantei
• ASP.NET Core mentine starea sesiuni printr-un cookie care este oferit
clientului, continand id-ul de sesiune, acesta fiind trimis aplicatiei cu
fiecare cerere
Sesiuni (II)
• Deoarece cookie-ul de sesiune este specific browserului, sesiunile nu
sunt disponibile pentru mai multe browsere
• Cookie-urile de sesiune sunt sterse cand se inchide browserul
• Sesiunea este mentinuta un timp limitat dupa ultima cerere.
• Se poate seta prin aplicatie timpul de expirare a sesiunii sau se
utilizeaza valoarea implicita 20 minute
Configurarea Session state
public class Startup • Distributed Memory Cache
{ public void (AddDistributedMemoryCache) –
ConfigureServices(IServiceCollection
services) { pentru a stoca items in memorie
services.AddDistributedMemoryCache(); • HttpContext.Session este disponibil
services.AddSession(options => dupa ce session state a fost
{options.IdleTimeout =
TimeSpan.FromSeconds(10 configurata
}); • HttpContext.Session nu poate fi
public void accesata inainte de apelul
Configure(IApplicationBuilder app, UseSession
IHostingEnvironment env) {
app.UseSession(); • IdleTimeout indica cat timp
sesiunea poate fi inactiva inainte sa
}
se renunte la continutul acesteia
• Aceasta setare se refera doar la
continutul sesiunii nu si la cookie
Modificarea setarilor initiale
services.AddSession(options =>
{
options.Cookie.Name = ".MyLibrary.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
}

Optiunea cookie determina setarile utilizate pentru a creea cookie-ul


Numele implicit este ".AspNetCore.Session"
Setarea si returnarea datelor de sesiune
public class IndexModel : PageModel
{
public string SessionKeyName = "_Name";
public string SessionKeyAge = "_Age";

public void OnGet()


{
if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
{
HttpContext.Session.SetString(SessionKeyName, "The Doctor");
HttpContext.Session.SetInt32(SessionKeyAge, 773);
}

var name = HttpContext.Session.GetString(SessionKeyName);


var age = HttpContext.Session.GetInt32(SessionKeyAge);
}
public class CreateModel : PageModel
TempData {
• Pastreaza datele pana in momentul in care sunt [TempData]
citite de alta cerere
public string Message { get; set; }
• Este util atunci cand se realizeaza redirectare,
datele fiind necesare pentru mai mult de o singura [BindProperty]
cerere
• Keep() – marcheaza itemii din dictionar pentru public Customer Customer { get; set; }
pastrare …
@page
@model IndexModel
<h1>Contacts</h1> _context.Customer.Add(Customer);
@{

if (TempData["Message"] != null)
{ Message = $"Customer {Customer.Name}
<h3>Message: added";
@TempData["Message"]</h3>
return RedirectToPage("./Contacts");
}
TempData.Keep("Message"); }
} }
Query strings
• Sunt adăugate la URL-ul cererii
• Query string: după semnul ?, variabilele sunt despărtite de &
http://www.mydomain.com?name1=value1&name2=value2&name3=value3
• Se poate trimite o cantitate limitata de date intre doua cereri

• Util cand dorim sa permitem link-urilor sa pasteze starea, aceste linkuri putand fi trimise
prin email sau retele de socializare

• Utilizatorii pot modifica usor valoarea query string pt ca ea este vizibilă in browser
• Valorile din query string trebuie validate
• URL query strings sunt publice, nu trebuie folosite pentru a trimite date sensitive
Request.Query["name1"]
Hidden Fields
• Sunt campuri de tip input puse in pagina HTML care nu sunt afisate
utilizatorului
<input type="hidden" value=""/>
• Proprietatea Value - se poate salva date

• Acestea sunt trimise la post back al urmatoarei cereri

• Este utila in cazuri in care avem formulare care se desfasoara pe mai


multe pagini
Curs 9
Autentificare si Autorizare Pagini Razor
Lect. dr. Florina Covaci
Autorizarea
• Procesul care determina ce actiuni ii sunt premise utilizatorului
• Independenta de autentificare dar necesita un mecansim de
autentificare
• Tipuri de autorizare:
• declararea rolului
• pe baza unei politici
Identity
• ASP.NET Core Identity:
• API care ofera functionalitati de tip login
• Gestioneaza utilizatori, parole, roluri, confirmare email etc.
• Configurat sa utilizeze o baza de date SQL Server pentru a stoca useri, parole
si date de profil
• Sub forma unei librarii de clase Razor
Areas
• Reprezinta un feature ASP.NET utilizat pentru organizarea unor
functionalitati relationate intr-un singur grup ca o structura de foldere
• Ofera o modalitate de partitionare a unei aplicatii web in grupuri
functionale mai mici, fiecare cu propriul ser de pagini razor, controller,
view-uri si modele
• Reprezinta efectiv o structura in interiorul unei aplicatii – pentru
aplicatii complexe e avantajos sa partitionam aplicatia in
functionalitati (arii) de nivel inalt
• Ex. Aplicatie de e-commerce – checkout, billing si search fiecare cu
propria arie care contine view-uri,controllere,modele
Configurarea Lockout
public async Task<IActionResult> OnPostAsync(string
returnUrl = null) {
if (ModelState.IsValid) {
var result = await
_signInManager.PasswordSignInAsync(Input.Email,
Input.Password, Input.RememberMe, lockoutOnFailure:
false);

}
Optiuni Lockout
• StartUp.ConfigureServices

services.Configure<IdentityOptions>(options => {
// Default Lockout settings.
options.Lockout.DefaultLockoutTimeSpan =
TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true; });

• AllowedForNewUsers – determina daca un utilizator nou poate fi blocat


• DefaultLockoutTimeSpan – perioada pentru care este blocat un utilizator
cand apare lockout
• MaxFailedAccessAttempts – numarul de incercari permise pana la lockout
Configurare Parola
• Implicit – Identity solicita ca parola sa contina un caracter litera mare,
un caracter litera mica, un numar, si un carcater non-alfanumeic
• Lungimea trebuie sa fie minim sase caractere – atributul
[StringLenght] din InputModel
(Areas/Identity/Pages/Account/Register.cshtml.cs si
ResetPassword.cshtml.cs)
• PasswordOptions in Startup.ConfigureServices.
Optiuni Parola
• services.Configure<IdentityOptions>(options => { // Default Password
settings. options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;});
Configurare Sign-in
services.Configure<IdentityOptions>(options =>
{ // Default SignIn settings.
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
});
Configurare User
services.Configure<IdentityOptions>(options => {
// Default User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123
456789-._@+";
options.User.RequireUniqueEmail = false; });
Autorizarea
• Procesul care determina ce actiuni ii sunt premise utilizatorului

• Independenta de autentificare dar necesita un mecansim de


autentificare
• Tipuri de autorizare in Pagini Razor:
• Conventie
• Atributul [Authorize]
Autorizarea prin conventie
• In fisierul Startup.cs
services.AddRazorPages(options => {
options.Conventions.AuthorizePage("/Contact");
options.Conventions.AuthorizeFolder("/Private");
options.Conventions.AllowAnonymousToPage("/Private/PublicPage");
options.Conventions.AllowAnonymousToFolder("/Private/PublicPages");
});
• Specificarea unei politici
options.Conventions.AuthorizePage("/Contact", "AtLeast21");
options.Conventions.AuthorizeFolder("/Private", "AtLeast21");
Solicitare autorizare area
• Autorizare pagina
options.Conventions.AuthorizeAreaPage("Identity",
"/Manage/Accounts");

Areas/Identity/Pages/Manage/Accounts.cshtml -> /Manage/Accounts


options.Conventions.AuthorizeAreaPage("Identity",
"/Manage/Accounts", "AtLeast21");

• Autorizare folder
options.Conventions.AuthorizeAreaFolder("Identity", "/Manage");
options.Conventions.AuthorizeAreaFolder("Identity", "/Manage",
"AtLeast21");
Combinare autorizare cu acces anonim
• Valid
.AuthorizeFolder("/Private").AllowAnonymousToPage("/Private/P
ublic")

• Invalid
.AllowAnonymousToFolder("/Public").AuthorizePage("/Public/Pri
vate")
Autorizarea prin atribut

• Aplicarea atributului AuthorizeAttribute la pagina

[Authorize(Policy = "RequireAdministratorRole")]
public class UpdateModel : PageModel
{ public ActionResult OnPost() { }
}

• Nu se poate aplica la nivel de metoda, doar la nivel de pagina


Specificare roluri pe baza unei politici (I)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthorization(options =>
{ options.AddPolicy("RequireAdministratorRole",
policy => policy.RequireRole("Administrator"));
});

}
Specificare roluri pe baza unei politici (II)
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthorization(options =>
{ options.AddPolicy("MoreRights", policy =>
policy.RequireRole("Administrator", "PowerUser", "BackupAdministrator"));
});

}
Autorizare claim based
• Cand o entitate este creata ii pot fi asociate una/mai multe claim-uri
(revendicare)
• Un claim – o pereche de tipul nume-valoare – reprezinta ceea ce e
subiectul (nu ceea ce face)
• Ex. Permis de conducere – data nastere -> claim
• Autorizarea bazata pe claim verifica valoarea acelui claim si permite
accesul la resurse pe baza acelei valori

• Ex. Permisiunea de a cumpara alcool


• O identiate poate contine mai multe claims
Adaugare claim
• Autorizarea pe baza de claim – este declarativa la nivel de controller
sau action method, specificand claims pe care un utilizator trebuie sa
le posede si valoarea pe care acel claim trebuie sa o detina pentru a
accesa o anumita resursa
• Cel mai simplu mod – autorizarea cauta prezenta unui claim fara a
cauta valoarea
• Inregistram o politica ca si parte a configurarii serviciului de autorizare
-> ConfigureServices
Politica EmployeeOnly verifica prezenta unui claim EmployeeNumber la
identitatea curenta

public void ConfigureServices(IServiceCollection services)


{ services.AddControllersWithViews();
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly",
policy => policy.RequireClaim("EmployeeNumber"));
});
}

Aplicam politica
[Authorize(Policy = "EmployeeOnly")]
public class UpdateModel : PageModel
{ public ActionResult OnPost() { }
}
Adaugare claim cu valori
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthorization(options =>
{ options.AddPolicy("Founders", policy =>
policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4",
"5"));
});
}
Autorizare pe baza unor politici
• Autorizarea pe baza de roluri si claims utilizeaza o serie de building-blocks – cerinta, handler de cerinta, o
politica preconfigurata
• O politica de autorizare contine una sau mai multe cereri – configurate ca parte din serviciul de autorizare

public void ConfigureServices(IServiceCollection services)


{ services.AddControllersWithViews(); services.AddAuthorization(options => {
options.AddPolicy("AtLeast18", policy => policy.Requirements.Add(new MinimumAgeRequirement(18)));
}); }
• Politica "AtLeast18" este creata; are o singura cerinta, a unei varste minime care este trimisa ca si parametru
• Aplicare politica
[Authorize(Policy = "AtLeast18")]
public class AlchoolPurchaseModel : PageModel
{ public ActionResult OnPost() { }
}
Cerinta si handler de cerinta
• O cerinta de autorizare – colectie de parametrii pe care o politica ii poate
folosi pentru a evalua utilizatorul current
using Microsoft.AspNetCore.Authorization;
public class MinimumAgeRequirement : IAuthorizationRequirement
{ public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge)
{ MinimumAge = minimumAge; }
}

• Daca o politica de autorizare contine mai multe cerinte de autorizare, toate


cerintele trebuie sa fie indeplinite pentru ca evaluarea politicii respective
sa fie realizata cu success – (AND)
public class MinimumAgeHandler :
AuthorizationHandler<MinimumAgeRequirement>
{ protected override Task
HandleRequirementAsync(AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{ if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth
&& c.Issuer == "http://myapp.com")) {return Task.CompletedTask; }
var dateOfBirth = Convert.ToDateTime( context.User.FindFirst(c =>
c.Type == ClaimTypes.DateOfBirth && c.Issuer ==
"http://myapp.com").Value);
int calculatedAge = DateTime.Today.Year - dateOfBirth.Year; if
(dateOfBirth > DateTime.Today.AddYears(-calculatedAge)) {
calculatedAge--; }
if (calculatedAge >= requirement.MinimumAge) {
context.Succeed(requirement); }
return Task.CompletedTask; }
}
Inregistrarea unui handler
public void ConfigureServices(IServiceCollection services) {
services.AddControllersWithViews();
services.AddAuthorization(options =>
{ options.AddPolicy("AtLeast18", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(18)));
});
services.AddSingleton<IAuthorizationHandler,
MinimumAgeHandler>(); }
Curs 10
Aplicatii mobile multi-platforma -
Xamarin.Forms
Lect. dr. Florina Covaci
Aplicatii mobile
• Dezvoltarea de aplicatii mobile se bazeaza in principal pe doua SO
• iOS de la Apple
• Android de la Google
• Dezvoltarea de aplicatii native
• pentru iOS se utilizeaza limbajele Objective-C si Swift si framework-urile
Cocoa si Cocoa Touch (API Apple)
• Pentru Android se utilizeaza Java si Android Software Development Kit de la
Google
• Putem utiliza C# si XAML fara a scrie cod in limbaje de programare
diferite –Xamarin.Forms
Ce este Xamarin.Forms?
• Framework multi-platforma care permite dezvoltarea facila de
aplicatii Android, iOS,UWP utilizand cod sursa comun
• Interfetele pot fi create in XAML folosind code-behind C#.
• Elementele de interfata cross-platform sunt incarcate si convertite in
controale native alte platformei pe care ruleaza aplicatia
• Codul principal este comun, fiecare proiect specific platformei pe care
ruleaza reprezinta instructiuni care pornesc framework-ul Xamarin
Structura unei solutii Xamarin.Forms
• AwesomeApp – Proiect de tip .NET Standard library
care contine codul comun si elementele UI comune
• AwesomeApp.Android – proiect care contine cod
Android specific reprezentand punctul de intrare in
aplicatia Android
• AwesomeApp.iOS – proiect care contine cod iOS
specific reprezentand punctul de intrare in aplicatia
iOS
Clasa App
• Pentru a maximiza reutilizarea codului, aplicatiile Xamarin.Forms
contin o singura clasa App
• Este responsabila pentru instantierea primei pagini care va fi afisata
de aplicatie pe fiecare platforma

public partial class App : Application


{ public App()
{ InitializeComponent();
MainPage = new NavigationPage(new ListEntryPage());
} ... } }
Lansarea aplicatiei pe iOS
• Pentru a lansa pagina initiala Xamarin.Forms in iOS, se defineste clasa
AppDelegate

public partial class AppDelegate :


global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{ public override bool FinishedLaunching(UIApplication app, NSDictionary
options)
{ global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(app, options);
} } }
Lansarea aplicatiei pe Android
• Pentru a lansa pagina initiala Xamarin.Forms in Android, se defineste clasa
MainActivity
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;

base.OnCreate(savedInstanceState);

Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
Crearea UI
• Interfata aplicatiilor Xamarin.Forms este alcatuita din obiecte care se
mapeaza pe controalele native ale fiecarei platforme targetate

• Grupuri de obiecte pentru crearea aplicatiilor in Xamarin.Forms:


• Pagini
• Layouts
• View-uri
• Celule
UI-Pagini Xamarin.Forms
• Deriva din clasa Page
• Control care ocupa tot ecranul dispozitivului
• Mai multe tipuri:
• ContentPage
• MasterDetailPage
• NavigationPage
• TabbedPage
• CarouselPage
ContentPage
• Cea mai simpla forma si cea mai
uzuala
• Continutul este reprezentat de
un singur obiect de tip View
(StackLayout, Grid, ScrollView)
MasterDetailPage
• Gestioneaza doua paneluri cu
informatie
• Master – afiseaza de obicei o
lista/meniu
• Detail – afiseaza un item slectat
pe pe pagina master
Pagina de tip MasterDetail
• Gestioneaza doua pagini: pagina master este in general o colectie de
date sau o lista de item-uri, iar pagina detail afiseaza un element
particular din colectie.
• Sunt definite ca si proprietati ale paginii MasterDetail, proprietati de
tip pagina denumite Master si Detail
• Modul in care MasterDetailPage afiseaza si comuta intre cele doua
pagini depinde de sistemul de operare, dispozitiv (telefon/tableta),
orientarea portet/landscape a device-ului si setarile proprietatii
MasterBehaviour
Comportamente posibile
• split: pagina master si detail sunt afisate una langa alta;
pagina master in stanga, iar pagina detail in dreapta
• popover: pagina detail va acoperi total sau partial pagina
master
• slide: se gliseaza intre pagina master si detail
• overlap: pagina detail acopera partial pagina master
• swap: pagina detail acopera complet pagina master.
NavigationPage
• Gestioneaza navigarea intre
paginile aplicatiei pe baza unei
arhitecturi de tip stiva

• Trimitem in constructorul lui


NavigationPage o instanta a
paginii principale

MainPage = new NavigationPage(new ListEntryPage())


Navigarea
• Cand utilizatorul navigheaza de la o pagina la alta, noua pagina este
adaugata pe stiva si devine pagina activa. Cand cea de a doua pagina
se intoarce la cea anterioara, este scoasa de pe stiva, iar ultima pagina
de pe stiva devine pagina activa
• Aplicatia are acces la stiva de navigare pe care Xamarin.Forms o
mentine pentru aplicatie avand acces la metode pentru manipularea
stivei- adaugarea/scoaterea de pagini
Navigarea ierharhica
• Pagini
• Modale
• NeModale
• In designul UI “modal” se refera necesitatea ca utilizatorul sa interactioneze
cu aplicatia inainte ca aplicatia sa continue
• Cand o fereastra modala este afisata utilizatorului, utilizatorul nu poate sa
se intoarca la ferestra principala, ci trebuie sa intractioneze cu fereastra
modala mai intai
• In general utilizam ferestre modale cand aplicatia are nevoie de informatii
de la utilizator si nu dorim ca utilizatorul sa se intoarca la pagina anterioara
decat dupa ce furnizeaza informatiile
• O fereastra care nu este modala se numeste fereastra nemodala
Navigarea ierarhica
• Mecanismul de navigare intre pagini din Xamarin.Forms
implementeaza pagini modale si modeless prin definirea a doua
metode care pot fi apelate de o pagina pentru a naviga catre alta
pagina
Task PushAsync(Page page) – navigheaza catre o pagina modeless
Task PushModalAsync(Page page) - navigheaza catre o pagina modala
• Pentru a naviga catre pagina anterioara sunt definite metodele:
Task<Page> PopAsync()
Task<Page> PopModalAsync()
Navigarea catre o alta pagina
• In general utilizam proprietatea Navigation a obiectului pagina, codul
pentru a naviga catre alta pagina va arata asa:

await Navigation.PushAsync(new MyNewPage());


await Navigation.PushModalAsync(new MyNewModalPage());
Obiectul Page
Proprietati:
• NavigationStack – avem acces la paginile modeless
• ModalStack – avem acces la paginile modale

• O pagina modeless poate naviga catre o pagina modala sau modeless


• O pagina modala poate naviga doar catre o alta pagina modala
Explorarea mecanismului de navigare
• Metodele Push si Pop returneaza obiecte de tip Task.
• In general vom utiliza await pentru a apela aceste metode
await Navigation.PushAsync(new ModelessPage());

• Presupunem ca avem o instructiune in cod dupa linia de mai sus. Cand se


executa?
• La finalizarea taskului PushAsync
• Cand se finalizeaza task-ul? Dupa ce utilizatorul a apasat butonul Back din
ModelessPage ?
• Finalizarea taskului nu se refera la faptul ca procesul de navigare s-a
terminat ci indica cand este sigura obtinerea starii curente a stivei cu
paginile de navigare
TabbedPage
• Permite navigarea intre paginile
copii utilizand tab-uri
• Proprietarea Children – este
setata catre o colectie de pagini
Pagina de tip Tabbed
• TabbedPage mentine o colectie de pagini copil, dintre care una
singura este in intregime vizibila la un moment dat
• Are doua proprietati care ajuta aplicatia sa retina care dintre paginile
din colectia de pagini copil este vizibila utilizatorului la un moment
dat:
• CurrentPage (de tip Page)
• SelectedItem (de tip object, adica un obiect din colectia ItemsSource)
• Defineste doua evenimente:
• PagesChanged - este declansat cand colectia ItemSource se modifica
• CurrentPageChanged – este declansat cand pagina vizibila se modifica
CarouselPage
• Permite navigarea intre paginile
copil pe baza de swipe

• Se seteaza proprietatea children


catre o colectie de Pagini
UI- Layout-uri Xamarin Forms

• Organizeaza controalele UI in structuri vizuale


• Tipuri:
• StackLayout
• Grid
• FlexLayout
StackLayout
• Organizeaza elementele intr-o stiva – orizontal/vertical
• Proprietatea Orientation specifica directia. In mod implicit orientarea
este verticala
• Este utilizat de obicei pentru a aranja subsectiunile unei
pagini
• Deseori utilizat ca un layout parinte , care contine
alte layout-uri copil
<StackLayout Margin="20">
<Editor Placeholder="Enter description"
Text="{Binding Text}"
HeightRequest="100" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Text="Save"
Clicked="OnSaveButtonClicked" />
<Button Grid.Column="1"
Text="Delete"
Clicked="OnDeleteButtonClicked"/>
</Grid>
</StackLayout>
Grid
• Utilizat pentru a afisa elemente in randuri si coloane
care pot avea dimensiuni absolute sau relative
• Randurile si coloanele sunt specificate cu proprietatile
RowDefinitions si ColumnDefinitions
• Pentru a pozitiona elemente specifice in grid se utilizeaza proprietatile
atasate Grid.Column si Grid.Row
• Pentru ca elementele sa ocupe mai multe randuri sau coloane se
utilizeaza proprietarile atasate Grid.RowSpan si Grid.ColumnSpan
FlexLayout
• Similar cu StackLayout – afiseaza elementele
orizontal/vertical
• Poate sa faca wrap la elementele copil, daca sunt
prea multe si nu incap intr-un singur rand sau coloana
• Permite control granular referitor la marime, orientare,
alinierea elementelor copil
FlexLayout
<FlexLayout Direction="Column" //elem.copil aranjate intr-o
singura coloana
AlignItems="Center" //fiecare elem. va fi centrat orizontal
JustifyContent="SpaceEvenly"> //spatiul liber ramas pe
verticala va fi impartit intre elemente
<Label Text="FlexLayout in Action" />
<Button Text="Button" />
<Label Text="Another Label" />
</FlexLayout>
UI- View-uri Xamarin.Forms
• Constituie “caramizile” aplicatiilor mobile multi-platforma

• Reprezinta obiecte de tip user-interface: label-uri, butoane, check-


boxuri, datepicker etc. (in alte tehnologii le regasim sub denumirea de
controale)
UI-Celule Xamarin.Forms
• Celula – element specializat pentru continutul unui tabel sau al unei
liste (ListView, TableView)
• Nu reprezinta in sine un element visual- reprezinta un template
pentru creare unui element vizual
Task-uri(I)
• Clasa Task din namespace-ul System.Threading.Tasks
• Sunt gestionate de un task scheduler pentru a fi rulate pe firul de
executie din background sau firul de executie al UI
• Similare cu o comanda care contine o actiune care poate fi executata
• Spre deosebire de o comanda, task-urile pot fi executate o singura
data si nu sunt declansate de actiunea unui utilizator ci de un task
scheduler care declanseaza actiunea pe firul de executie
corespunzator
Task-uri (II)
• Implicit task-urile utilizeaza un task scheduler care ruleaza taskurile pe
un fir de executie din background
• Task scheduler-ul default detine un set de fire de executie de
background si utilizeaza urmatorul fir de executie disponibil pentru a
executa urmatorul task
Async si Await (I)
• Cuvintele cheie async si await – introduse in C# 5.0
• Acestea nu fac nimic/nu executa o actiune in termeni de cod
• Sunt folosite de compilator, codul marcat de aceste cuvinte fiind gestionat
diferit
• Async – prescurtarea de la asynchronous—se pot executa mai multe actiuni
simultan (cod multithreaded – cod asynchron)
async void OnNoteAddedClicked(object sender, EventArgs e)
{
await Navigation.PushAsync(new NotePage
{
BindingContext = new Agenda()
});
}
Async si Await (II)
• Permit modelarea urmatorului pattern “ruleaza codul x in background
apoi ruleaza codul y in contextul curent sincronizat”
• Marcam o metoda async si cand dorim sa apelam alte metode async
din aceasta metoda marcam aceste apeluri cu await
• Await specifica compilatorului ca undeva in metoda pe care o apelam,
o parte din cod se va executa pe un fir de executie din background
utilizand un task si sa astepte rularea restului de cod din metoda pana
cand metoda marcata await termina ce are de facut
• Intre timp, thread-ul curent poate procesa cealalta parte a codului si
dupa finalizarea executiei metodei marcate cu await se executa si
cealalta parte
Task in background

public override void ViewDidLoad()


{
...
Task.Delay(TimeSpan.FromSeconds(5));
TextField.Text = "Foo";
}

Task-ul Delay task este creat ,


este pornit intr-un thread de background, UI este actualizat
Adaugam async si await
public override async void
ViewDidLoad()
{
...
await
Task.Delay(TimeSpan.FromSecon
ds(5));
TextField.Text = "Foo"; • Contextul de sincronizare UI are un singur thread,
} deci daca marcam await o metoda din thread-ul
UI, codul care urmeaza dupa metoda va rula pe
• Se iese din metoda ViewDidLoad thread-ul UI
de indata ce metoda await este • Daca marcam await o metoda dintr-un thread de
apelata, iar restul codului se background, codul de dupa await s-ar putea sa
executa in firul de executie original ruleze pe un thread diferit in background
Curs 11
Aplicatii mobile multi-platforma
Xamarin.Forms

Lect. dr. Florina Covaci


Accesul la Date
• Cele mai multe aplicatii mobile necesita salvarea
unor date local

• SQLite – database engine

• Pachetul NuGet SQLite.Net


• Adaugam o proprietate in clasa App – database – calea catre fisierul local de tip baza de
date
static TodoItemDatabase database;
public static TodoItemDatabase Database
{
get
{
if (database == null)
{
database = new TodoItemDatabase(

Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalAppl
icationData), "TodoSQLite.db3"));
}
return database;
}
}
• Expunem baza de date ca un singleton – o singura conexiune la baza de date este creata
si este mentinuta deschisa cat timp aplicatia ruleaza, evitand operatia costisitoare de
deschidere/inchidere db la fiecare operatie pe bd
Creare Tabel
• Constructorul primeste ca si parametru calea spre fisierul de tip baza
de date

public TodoItemDatabase(string dbPath)


{
database = new SQLiteAsyncConnection(dbPath);
database.CreateTableAsync<TodoItem>().Wait();
}
Behaviors
• Permit adaugarea de functionlitati la controalele de interfata
• Functionalitatea este implementata intr-o clasa de tip Behaviour si
atasata controlului respectiv ca si cum ar fi o parte a controlului
• Exemple de utilizare:
• Crearea unui validari pentru un control de tip Entry
• Controlul unui animatii
• Adaugarea de efecte la un control
Attached Behaviors
• Clase statice cu una sau mai multe proprietati atasate
• O proprietate atasata este un tip special de proprietate- sunt definite
intr-o clasa, dar sunt atasat altor obiecte
• Se pot recunoaste in attribute XAML care contin numele unei
clase.numeproprietate
public static class NumericValidationBehavior
{ public static readonly BindableProperty
AttachBehaviorProperty = BindableProperty.CreateAttached (
"AttachBehavior", typeof(bool),
typeof(NumericValidationBehavior), false,
propertyChanged:OnAttachBehaviorChanged);
public static bool GetAttachBehavior (BindableObject view)
{ return (bool)view.GetValue (AttachBehaviorProperty); }
public static void SetAttachBehavior (BindableObject view,
bool value)
{ view.SetValue (AttachBehaviorProperty, value); }
• Contine o proprietate atasata denumita AttachBehavior cu un setter si
un getter static
• Inregistreaza metoda OnAttachBehaviorChanged care va fi executata
cand valoarea proprietatii se modifica
static void OnAttachBehaviorChanged (BindableObject view, object oldValue, object
newValue)
{ var entry = view as Entry;
if (entry == null) { return; }
bool attachBehavior = (bool)newValue;
if (attachBehavior)
{ entry.TextChanged += OnEntryTextChanged; }
else { entry.TextChanged -= OnEntryTextChanged; }
}
static void OnEntryTextChanged (object sender, TextChangedEventArgs args)
{ double result;
bool isValid = double.TryParse (args.NewTextValue, out result);
((Entry)sender).TextColor = isValid ? Color.Default : Color.Red; }

• Inregistreaza/de-inregistreaza un event handler pentru evenimentul TextChanged pe baza valorii


proprietatii atasate AttachBehavior
• <Entry Placeholder="Enter a System.Double"
local:NumericValidationBehavior.AttachBehavior="true" />
• <Entry Placeholder="Enter a System.Double"
local:NumericValidationBehavior.AttachBehavior="false" />
Xamarin.Forms behaviors
• Cream o clasa care mosteneste clasa Behavior
• Suprascriem metoda OnAttachedTo
• Suprascriem metoda OnDetachingFrom
• Implementam functionalitatea behavior-ului
public class NumericValidationBehavior : Behavior<Entry>
{ protected override void OnAttachedTo(Entry entry) { entry.TextChanged
+= OnEntryTextChanged; base.OnAttachedTo(entry); }

protected override void OnDetachingFrom(Entry entry) { entry.TextChanged


-= OnEntryTextChanged; base.OnDetachingFrom(entry); }

void OnEntryTextChanged(object sender, TextChangedEventArgs args) {


double result; bool isValid = double.TryParse (args.NewTextValue, out
result); ((Entry)sender).TextColor = isValid ? Color.Default : Color.Red;
} }

• Metoda OnAttachedTo este apelata imediat ce behavior este atasat unui


control
• Metoda OnDetachingFrom este apelata imediat ce behavior-ul este
inalturat
• Primesc ca parameter o referinta catre controlul care este atasat
Consumarea unui behavior Xamarin.Forms
<Entry Placeholder="Enter a System.Double">
<Entry.Behaviors> <local:NumericValidationBehavior />
</Entry.Behaviors>
</Entry>

var entry = new Entry { Placeholder = "Enter a System.Double" }; entry.Behaviors.Add (new


NumericValidationBehavior ());

var toRemove = entry.Behaviors.FirstOrDefault (b => b is NumericValidationBehavior); if


(toRemove != null) { entry.Behaviors.Remove (toRemove); }

entry.Behaviors.Clear();
Ciclul de viata al aplicatiei
• Clasa Application contine trei metode care pot fi suprascrise pentru a
raspunde modificarilor din ciclul de viata al aplicatiei
• OnStart – apelata cand aplicatia porneste.
• OnSleep – apelata de fiecare data cand aplicatia se muta in background.
• OnResume – apelata cand se face resume la aplicatie, dupa ce a fost trimisa in
background.
• Nu exista o metoda dedicata pentru terminarea aplicatiei
• In cirmcumstante normale (nu are loc un crash), terminarea aplicatiei
se va realiza din starea Onsleep
Persistarea datelor intre starile aplicatiei
• Clasa Application are o proprietate de tip dictionar:
Properties - poate fi folosita pentru a stoca date intre
diferite stari ale aplicatiei
• Dictionarul utilizeaza o cheie de tip string si salveaza
valoarea unui obiect
• Dictionarul este salvat pe dispozitiv in mod automat si
este repopulat cand se realizeaza repornirea aplicatiei
public partial class App : Application
{ const string displayText = "displayText";
public string DisplayText { get; set; }
public App() { ...}
protected override void OnStart() {
Console.WriteLine("OnStart");
if (Properties.ContainsKey(displayText)) {
DisplayText = (string)Properties[displayText]; }
}
protected override void OnSleep() {
Console.WriteLine("OnSleep");
Properties[displayText] = DisplayText; }
Nu este necesara restaurarea datelor la OnResume deoarece
cand aplicatia se afla in background datele sunt in memorie
Libraria Xamarin.Essentials
• Multe din functionalitatile oferite de aplicatiile mobile se bazeaza pe
noi tipuri de date pot fi colectate de la device-urile mobile (localizare
GPS etc.)
• Xamarin.Essentials ofera un API unic multi-platforma pentru aplicatii
Android, iOS sau UWP, care poate fi accesat din codul comun
• Xamarin.Essentials - NuGet package – il downloadam
• Adaugam directiva: using Xamarin.Essentials;
Clasa Geocoding

• Ofera posibilitatea de a gasi coordonatele pentru o adresa si invers:


gasirea adresei pentru un set de coordonate
• Proprietati: Latitudinea, Longitudinea si Altitudinea
• Altitudinea nu este intotdeauna disponibila – proprietatea Altitude
poate fi null sau 0. Daca este valabila, valoarea indica metri deasupra
nivelului marii
Clasa Geocoding (II)
try
{
var address = "FSEGA Theodor Mihali Cluj-Napoca";
var locations = await Geocoding.GetLocationsAsync(address);

var location = locations?.FirstOrDefault();


if (location != null)
{
Console.WriteLine($"Latitude: {location.Latitude},
Longitude: {location.Longitude}, Altitude: {location.Altitude}");
}
}
catch (FeatureNotSupportedException fnsEx)
{
// Feature not supported on device
}
Reverse Geocoding (I)
• Furnizeaza detalii legate de un obiectiv, pentru un set de coordinate furnizat:
• AdminArea - Judet
• CountryCode – Codul tarii (Ex. RO)
• CountryName – Nume tara
• FeatureName – Numele obiectivului de la acele coordonate/ Numarul
• Locality - Localitate
• PostalCode – Cod Postal
• SubAdminArea – Nume municipiu
• SubLocality – Nume Comuna
• SubThoroughfare – Numarul/ Interval Numeric
• Thoroughfare – Strada/Artera
Reverse Geocoding (II)
try
{ var lat = 47.673988; var lon = 23.620944;;
var placemarks = await Geocoding.GetPlacemarksAsync(lat, lon);
var placemark = placemarks?.FirstOrDefault();
if (placemark != null) {
var geocodeAddress = $"AdminArea: {placemark.AdminArea}\n" + $"CountryCode:
{placemark.CountryCode}\n" +
$"CountryName: {placemark.CountryName}\n" +
$"FeatureName: {placemark.FeatureName}\n" +
$"Locality: {placemark.Locality}\n" +
$"PostalCode: {placemark.PostalCode}\n" +
$"SubAdminArea: {placemark.SubAdminArea}\n" +
$"SubLocality: {placemark.SubLocality}\n" +
$"SubThoroughfare: {placemark.SubThoroughfare}\n" +
$"Thoroughfare: {placemark.Thoroughfare}\n";
Console.WriteLine(geocodeAddress); }
} catch (FeatureNotSupportedException fnsEx) { // Feature not supported on device }
Clasa Geolocation
• Putem afla coordonatele curente de geolocalizare a dispozitivului
• Pentru a folosi functia de geolocalizare sunt necesare anumite actiuni
de setup specific platformei
• Ex. Android:
• Permisiunea pentru localizare trebuie setata la proiectul Android
• Daca aplicatia targheteaza >=Android 5.0 trebuie sa specificam ca
applicatia utilizeaza features hardware
Set-up pentru Android
• Se adauga in fisierul AssemblyInfo.cs
[assembly:
UsesPermission(Android.Manifest.Permission.AccessCoarseLocation)]
[assembly: UsesPermission(Android.Manifest.Permission.AccessFineLocation)]
[assembly: UsesFeature("android.hardware.location", Required = false)]
[assembly: UsesFeature("android.hardware.location.gps", Required = false)]
[assembly: UsesFeature("android.hardware.location.network", Required =
false)]

API-ul pentru geolocalizare va cere permisiunea utilizatorului atunci


cand este necesar
Gelocalizare (I)
• Putem gasi ultima locatie cunoscuta a dispozitivului prin apelul metodei
GetLastKnownLocationAsync .
• Este mai rapida decat a executa o interogare noua, dar mai putin exact si poate returna null daca
nu exista locatii salvate in cache

try { var location = await Geolocation.GetLastKnownLocationAsync();


if (location != null)
{ Console.WriteLine($"Latitude: {location.Latitude}, Longitude:
{location.Longitude}, Altitude: {location.Altitude}"); }
}
catch (FeatureNotSupportedException fnsEx)
{ // Handle not supported on device exception }
catch (FeatureNotEnabledException fneEx)
{ // Handle not enabled on device exception }
catch (PermissionException pEx) { // Handle permission exception }
Geolocalizare (II)
• Pentru a afla coordonatele locatiei curente utilizam GetLocationAsync
try {
var request = new
GeolocationRequest(GeolocationAccuracy.Medium);
var location = await
Geolocation.GetLocationAsync(request);
if (location != null) {
Console.WriteLine($"Latitude: {location.Latitude},
Longitude: {location.Longitude}, Altitude:
{location.Altitude}");
}
} catch (FeatureNotSupportedException fnsEx) { //
Handle not supported on device exception }
Acuratetea geolocalizarii
Lowest Low Medium High

Android 500 500 100-500 0-100

iOS 3000 1000 100 10

UWP 1000-5000 300-3000 30-500 <=10


Distanta intre doua locatii
• Clasa Location defineste metoda CalculateDistance – permite sa
calculam distanta intre doua locatii geografice. Distanta calculata nu ia
in considerare drumurile ci distanta intre doua puncte pe suprafata
Pamantului

Location boston = new Location(42.358056, -71.063611);


Location sanFrancisco = new Location(37.783333, -122.416667);
double miles = Location.CalculateDistance(boston,
sanFrancisco, DistanceUnits.Kilometers);
Clasa Map (I)
• Permite unei aplicatii sa deschida o aplicatie de tip “harti” instalata la o
locatie specificata
• Utilizeaza metoda OpenAsync cu parameterii de tip Location sau Placemark
si optional un parametru de tip MapLaunchOptions

public class MapTest


{ public async Task NavigateToBuilding25() {
var location = new Location(47.645160, -122.1306032); var
options = new MapLaunchOptions { Name = "Microsoft Building
25" };
await Map.OpenAsync(location, options);
}
}
Clasa Map (II)
• Cand utilizam metoda OpenAsync cu un Placemark este necesara
furnizarea urmatoarelor informatii:
• CountryName
• AdminArea
• Thoroughfare
• Locality
public class MapTest {
public async Task NavigateToBuilding25()
{ var placemark = new Placemark { CountryName =
"United States", AdminArea = "WA", Thoroughfare =
"Microsoft Building 25", Locality = "Redmond" };
var options = new MapLaunchOptions { Name =
"Microsoft Building 25" };
await Map.OpenAsync(placemark, options); } }
Harta- mod navigare
• Daca se apeleaza OpenMapAsync fara parametrul
MapLaunchOptions, harta va fi lansata cu locatia specificata. Optional
putem sa calculam ruta de navigare din pozitia curenta la care se afla
dispozitivul prin setare NavigationMode la MapLaunchOptions
public class MapTest {
public async Task NavigateToBuilding25()
{ var location = new Location(47.645160, -122.1306032);
var options = new MapLaunchOptions { NavigationMode =
NavigationMode.Driving };
await Map.OpenAsync(location, options); }
}
• NavigationMode suporta Bicycling, Driving si Walking
Pattern-uri pentru transferul de date (I)
• Necesitatea ca in cadrul unei aplicatii multi-pagina, aplicatia sa foloseasca
date comune si in mod particular o pagina sa trimita date spre alta pagina
• Argumentele constructorului – o pagina navigheaza spre alta pagina, trimite date
celei de a doua utilizand constructorul acesteia
public partial class SchoolPage : ContentPage
{ public SchoolPage()
{ …}
async void OnListViewItemSelected(object sender,
SelectedItemChangedEventArgs args) {
Student student = args.SelectedItem as Student;
if (student != null) {await Navigation.PushAsync(new
StudentPage(student)); }
Xamarin.Forms MessagingCenter
• Implementeaza patternul publish-subscribe :
• publishers trimit mesaje fara a cunoaste destinatarii (subsribers);
• similar subscribers asculta mesaje fara a cunoaste publisherii

• Acest mecanism permite comunicarea fara ca publishers si


subscribers sa aiba o referinta unul catre celalalt – se reduce
dependenta
Messaging Center

Mai multi publishers pot publica acelasi mesaj si mai multi subcribers
pot asculta acelasi mesaj
Pattern-uri pentru transferul de date (II)
• Clasa MessagingCenter – clasa statica care contine trei metode Subscribe,
Unsubscribe si Send.
• Mesajele sunt identificate printr-un string si sunt insotite de un obiect
• Constructorul subscrie la un mesaj identificat prin string-ul “Infor-
mationReady.”
• Argumentele indica obiectul care primeste mesajul, numele mesajului si o
expresie Lambda
public HomePage() {
...
MessagingCenter.Subscribe<InfoPage, Information> (this,
"InformationReady", (sender, info) => {
int index = list.IndexOf(info);
if (index != -1) { list[index] = info; } else { list.Add(info); }
});
MessageCenter (II)
async void OnListViewItemSelected(object sender,
SelectedItemChangedEventArgs args)
{ if (args.SelectedItem != null)
{listView.SelectedItem = null;
InfoPage infoPage = new InfoPage();
await Navigation.PushAsync(infoPage);
MessagingCenter.Send<HomePage, Information> (this, "InitializeInfo",
(Information)args.SelectedItem); }
Metoda Send trimite mesaje care sunt primite de toti subscriberii la acel
mesaj
Argumentele metodei Send indica obiectul care trimite mesajul, numele
mesajului si datele care sunt reprezentate de SelectedItem din ListView.
Curs 12
Servicii Web

Lect. dr. Florina Covaci


API web
• Scop - transferul de date, independent de limbaj sau framework-ul
folosit
• ASP.NET Core suporta crearea de servicii REST, cunoscute si sub
denumirea de API-uri web
• Pentru a gestiona cereri, un API web utilizeaza controale
• Controalele intr-un API web sunt clase care deriva din clasa
ControllerBase
Representational State Transfer
• Roy Fielding

Architectural Styles and the Design of Network-based Software


Architectures, 2000
https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
REST
• Un stil arhitectural care defineste un set de reguli pentru a construi
servicii web
• Stil architectural = concept cu principii predefinite
• Implementare principilor care reprezinta componente in aplicatie
• Implementarea REST va fi diferita de la dezvoltator la dezvoltator – nu
exista un stil de implementare fixat
• Diferit de pattern-uri arhitecturale precum MVC, care reprezinta
concepte si implementari concrete. MVC este un pattern arhitectural
avand o structura fixata care defineste cum componentele
interactioneaza unele cu altele si nu pot fi implementate diferit
Serviciu REST
Caracteristici REST
• Raspunsul trimis de la server ca urmare a cererii clientului este o
resursa intr-un anumit format
• Cele mai comune formate sunt: .json, .xml, .pdf, .doc
• REST este stateless- starea sistemului este intodeauna diferita : cand
se primeste o cerere la server, cererea este gestionata si apoi uitata,
asftel ca urmatoarea cerere nu depinde de starea celei anterioare.
• Fiecare cerere este gestionata de server in mod independent
• Cererile sunt realizate folosind o conexiune HTTP, fiecare continand
un URI (Uniform Resource Identifier) pentru localizarea resursei
solicitate
Constrangeri REST
1. Arhitectura client-server
• Clientul (consumatorul serviciului) nu trebuie sa cunoasca detalii despre
cum serverul proceseaza datele si le stocheaza in baza de date

• Serverul nu trebuie sa depinda de implementarea clientului, in special de


UI
• Clientul si serverul nu sunt una si aceasi entitate si fiecare poate exista in
lipsa celuilalt

• Serviciul cand interactioneaza cu clientii, ofera suficiente informatii: cum


sa fie consumat, ce operatii se pot efectua la utilizarea lui
• Clientul si serverul pot fi complet decuplate daca adera la toate
constrangerile REST
2. Stateless
• Un serviciu REST nu mentine starea aplicatiei =>Stateless
• O cerere intr-un serviciu REST nu depinde de cererile anterioare. Serviciul
trateaza fiecare cerere independent

3. Caching
• Caching trebuie sa fie aplicat cand este posibil, iar resursele trebuie sa se
autodeclare cacheable.
• Daca sunt cacheable, serverul stie durata pentru care raspunsul este valid
• Daca clientul are acces a un raspuns din cache valid, nu se mai executa cererea si
se utilizeaza copia din cache
• Specificare explicita a unei resurse cachable se face in headerul Cache-Control
unde se poate seta si durata valabilitatii copiei
• Imbunatatirea performantei pe partea de client si o mai buna scalabilitate pe
partea de server deoarece se reduce incarcarea
4. Code on demand (optional)
• De cele mai multe ori serviciul returneaza reprezentari statice de resurse -
.json, .xml
• Serviciul poate returna cod executabil pentru a extinde functionalitatea
aplicatiei

5. System multi nivel


• Fiecare nivel este restrictionat sa acceseze doar urmatorul nivel din
ierarhie
• Deployment-ul API-urilor pe serverul A, stocarea datelor pe serverul B si
cererile de autenitificare pe serverul C – clientul nu stie unde e conectat
• Arhitectura bazata pe layere – ajuta la o mai buna gestionare a
complexitatii si imbunatateste mentenabilitatea codului.
6. Interfata uniforma
• Decuplarea clientului de serviciu
• REST este definit de patru constrangeri pentru interfata:
• Identificarea resurselor: Un URI este utilizat pentru a identifica o
resursa
• Manipularea resurselor prin reprezentari: Cand un client detine o
resursa are suficienta informatie pentru a modifica sau sterge resursa
• Mesaje auto-descriptive: Mesajele trimise trebuie sa contina
suficienta informatie despre datele procesate.
• Hypermedia ca si motor al starii aplicatiei: Reprezentarea returnata
de serviciu poate sa contina link-uri catre alte resurse
Metode
Metoda Operatia realizata pe server Tipul Metodei
GET Citeste/Incarca o resursa Safe
PUT Insereaza sau actualizeaza o resursa Idempotent
daca aceasta exista
POST Insereaza sau actualizeaza o resursa Nonidempotent
daca aceasta exista
DELETE Sterge o resursa Idempotent
OPTIONS Afiseaza o lista cu operatiile permise Safe
pentru o resursa
HEAD Returneaza doar Headerul fara body Safe
pentru cererea respectiva
Tipuri de metode
• Safe – operatia executata nu afecteaza valoarea initiala a resursei
• GET, OPTIONS, HEAD – doar incarca sau citesc resursa
• idempotent (poate fi repetata) – operatia executata are acelasi
rezultat indiferent de cate ori o executam
• DELETE si PUT operereaza pe o resursa specifica, operatia putand fi
repetata
POST vs. PUT
• Ambele pot sa adauge sau sa actualizeze o resursa
• POST este nonidempotent, nu este repetabil – va crea de fiecare data
o noua resursa daca nu se trimite URI-ul exact
• PUT valideaza existenta resursei prima data, iar daca exista o
actualizeaza, in caz contrar o creaza
• PUT https://localhost:44327/api/shoplists- nu merge pt ca nu are ID-
ul resursei specificat
• PUT https://localhost:44327/api/shoplists/5 - create/update

• POST https://localhost:44327/api/shoplists- creeaza o noua resursa;


la apeluri ulteriorare – inregistrari duplicat, creaza o noua resursa cu
aceleasi date
• POST https://localhost:44327/api/shoplists/18- update

• PUT creaza sau actualizeaza o resursa pentru un URI specificat


• PUT si POST se comporta la fel daca resursa exista deja
• POST fara specificarea ID-ului creaza o resursa de fiecare data cand se
executa
Avantaje REST
• Independenta fata de o platforma sau un limbaj de programare
• Metode standardizate prin utilizarea HTTP
• Nu pastreaza starea clientului pe server
• Suporta caching
• Accesibil tuturor tipurilor de aplicatii client: Mobile, web, desktop
Dezavantaje REST
• Daca standardele nu sunt aplicate corect vor exista dificultati pentru
clientul care consuma serviciul
• Securitatea este o preocupare, daca nu exista procese care sa
restrictioneze accesul la resurse
Coduri de stare
• Cand serverul returneaza raspunsul returneaza si codul de stare pentru a informa clientul
despre cum s-a executat cererea pe server

• 200 OK Raspuns standard pentru cereri HTTP executate cu success


• 201 CREATED Raspuns standard pentru o cerere cand o resursa a fost creata cu success
• 204 NO CONTENT Raspuns standard pentru cereri executate cu success dar nu s-a
returnat nimic in body
• 400 BAD REQUEST Cererea nu poate fi procesata datorita sintatxei gresite, marimii prea
mari sau alt motiv
• 403 FORBIDDEN Clientul nu are permisiunea sa acceseze resursa solicitata
• 404 NOT FOUND Resursa nu exista
• 500 INTERNAL SERVER ERROR Cand apare o eroare sau o exceptie la procesarea codului
pe server
Coduri de stare implicite

• GET: Returneaza 200 OK


• POST: Returneaza 201 CREATED
• PUT: Returneaza 200 OK
• DELETE: Returneaza 204 NO CONTENT daca operatia a esuat
RESTful APIs
• Un serviciu web care respecta principiile REST se numeste RESTful API
• In mod uzual serviiciile web RESTful utilizeaza mesaje JSON pentru a
returna date clientului.
• JSON – format de date de tip text de dimensiuni reduse – data-
interchange
{
"id":1
"amount": 8.50,
"date":"2020-05-09T00:00:00Z",
"description": "tea"
}
Maparea URI
• Cand un Web API primeste o cerere ruteaza cererea respectiva la o
actiune
• Actiunile – metode in clasa Controller
• Gaseste URI pe baza specificarilor de rutare definite astfel:
• Pentru a gasi controllerul adauga “controller” la valoarea variabilei
{controller}-shoplists
• Pentru a gasi actiunea cauta in actiunile definite in controller care sunt
marcate cu acelasi atribut HTTP
• {id} este mapat cu parametrul unei actiuni.
• https://192.168.1.9:45455/api/shoplists/{0}
Atribute
• [ApiController] – aplicat la controale specifice

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
[ApiController] – controale multiple
• Cream o clasa de baza

[ApiController] public class MyControllerBase :


ControllerBase { }

[Route("[controller]")]
public class PetsController : MyControllerBase
Atributul [Route]
• Cand utilizam atributul [ApiController] este necesar atributul [Route]

[ApiController]
[Route("[api/controller]")]
public class WeatherForecastController : ControllerBase

Action methods sunt inaccesibile prin rutele conventionale specificate


in Startup.Configure
Tipul de return
• ActionResult
Mai multe tipuri de ActionResult sunt posibile intr-o actiune.

Tipurile ActionResult reprezinta diferite coduri HTTP status:


• BadRequestResult(400) / BadRequest())
• NotFoundResult(404)
• OkObjectResult(201)
Clasa HttpClient si HttpResponseMessage
• Clasa HttpClient este utilizata pentru a trimite si primi cereri folosind
protocolul HTTP
• Ofera functionalitati pentru a trimite cereri HTTP si a primi raspunsuri
HTTP de la un URI
• Clasa HttpResponseMessage reprezinta un mesaj de tip raspuns
primit de la un serviciu web in urma realizarii unei cereri HTTP
• Contine informatii despre raspuns, incluzand codul de stare
• Continutul poate fi citit cu metoda ReadAsStringAsync
Crearea unui serviciu REST cu ASP.NET Core
GET
[Produces("application/json")]
[ApiController] //indica faptul ca clasa va fi utilizata pentru raspunsuri HTTP
[Route("[controller]")] //conventie de nume – la runtime se va concatena cu numele textului din URL
public class ShopListsController : ControllerBase
{
[HttpGet("{id}")] //identifica o actiune asociata cu metoda GET
public async Task<ActionResult<ShopList>> GetShopList(int id)
{
var shopList = await _context.ShopList.FindAsync(id);
if (shopList == null)
{
return NotFound();
}
return shopList;
}
POST
[HttpPost]
public async Task<ActionResult<ShopList>>
PostShopList(ShopList shopList)
{
_context.ShopList.Add(shopList);
await _context.SaveChangesAsync();

return CreatedAtAction("GetShopList", new { id =


shopList.ID }, shopList); // returneaza codul de stare 201

}
PUT
[HttpPut("{id}")] //primeste ID-ul din URL

public async Task<IActionResult> PutShopList(int id, ShopList shopList)


{
if (id != shopList.ID)
{
return BadRequest();
}

_context.Entry(shopList).State = EntityState.Modified;

try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ShopListExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
DELETE
[HttpDelete("{id}")]
public async Task<ActionResult<ShopList>> DeleteShopList(int id)
{
var shopList = await _context.ShopList.FindAsync(id);
if (shopList == null)
{
return NotFound();
}

_context.ShopList.Remove(shopList);
await _context.SaveChangesAsync();

return shopList;
}
Arhitectura orientata pe servicii
• Separarea responsabilitatilor din punct de vedere business-oriented
in servicii independente
• Fiecare componenta are propria
logica de business si poate fi
implementata separat

• Expunem API-uri RESTful care


pot fi consumate de orice client
Consumarea serviciilor web
• Serviciile REST pot sa fie sau nu
parte a unei aplicatii Web
• O aplicatie Web poate sa apeleze sau
sa consume serviicii web externe sau
servicii care fac parte din aceasi aplicatie
• Programul care permite interactiunea
(cerere,raspuns) intre serviciu si aplicatia
care consuma acel serviciu se numeste client
Curs 13
Pattern-urile MVVM si MVC
Lect. dr. Florina Covaci
Decuplarea UI de code cu data binding
• Data binding – relatii intre date si controale UI – acestea raman
sincronizate cand unul din cele doua elemente se modifica
• Bindings – obiecte intermediare intre sursa si target
Modificarea target-ului
• Obiectul target trebuie sa mosteneasca BindableObject, si
proprietatea target trebuie sa fie BindableProperty
• BindableProperty are un mechanism de notificare built-in care ridica
un eveniment cand se modifica valoarea acestuia – obiectul binding
stie ca s-a modificat valoarea, preia valoarea si o trimite sursei
Modificarea sursei
• Se modifica o proprietate din obiectul sursa - provocare pentru ca nu
exista un mechanism built-in pentru a cunoaste care din proprietati a
fost modificata

public class ShopList


{
public int ID { get; set; }
public string Description { get; set; }
public DateTime Date { get; set; }
}
Modificarea sursei
• =>scriem cod care invoca un eveniment cand oricare din prorietatilor obiectului sursa se modifica.
• Implementam interfata INotifyPropertyChanged in clasa noastra; interfata are un eveniment denumit PropertyChanged
• Obiectele de tip binding cauta automat aceasta interfata in obiectul sursa si subscriu la acest eveniment

public class ShopList : INotifyPropertyChanged


{
public event PropertyChangedEventHandler PropertyChanged;
string description;
public string Description {
get { return description; }
set { if (description != value) {
description = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Description));
}
}
}
... }
Pattternul MVVM
• Aplicatiile care nu utilizeaza MVVM – fisiere de code-behind
{something}.xaml.cs
• Codul din fisiere de code-behind de obicei controleaza
comportamentul interfetei utilizator – modificarea unei culori,
modificarea unui text – handlere =>unit tests dificil de realizat
• Cand este utilizat adecvat- MVVM inlatura acest neajuns- logica
comportamentului UI se afla in clase care pot fi testate cu unit testing
– putem realiza data-bind la fiecare element de UI cu un viewmodel si
sa eliminam(aproape) codul din fisierele de code-behind
Partile unei aplicatii MVVM
• Model
• View
• ViewModel

• Semnificatia acestora este consistenta cu patternul Model-View-


Controller(MVC)
MVVM
Modelul
• Intr-o aplicatie MVVM, modelul se refera la date si logica de business.
• Modelul nu se preocupa de modul in care aplicatia este prezentata
utilizatorului
• Felul in care e descris modelul trebuie sa permita portarea aplicatiei
de la o aplicatie mobila la o aplicatie web/ WPF si sa utilizeze acelasi
model
• Ex. Clasa Employee si clasa Department. Se poate include si o clasa
EmployeeRepository pentru a scrie logica legata de persistenta
datelor. Alte pattern-uri considera repositories separate de modeul,
dar in MVVM ne referim de obicei la orice legat de business logic sau
business data ca fiind parte din model
Model - exemplu
public class Employee
{
public int Id { get; }
public string Name { get; set; }
public Employee Supervisor { get; set; }
public DateTime HireDate { get; set; }
public void GetVacationBalanceInDays () { ... }
}
public class EmployeeRepository
{ public IList<Employee> QueryEmployees() { ... } ... }
Views
• View-urile controleaza elementele care interactioneaza direct cu
utilizatorul
• Ex. Clicks, taps
• Controale : butoane, entry, stiluri, fonturi
ViewModel - Exemplu
Model : realizeaza logica de business
public class Employee
{
public decimal GetVacationBalanceInDays()
{ //Calculam cate zile de concediu mai are ex. 13 zile... }
ViewModel: intermediar, expune public proprietati necesare view-ului cu care se realizeaza bind
public class EmployeeViewModel
{
private Employee _model;
...
public string FormattedVacationBalance
{
get { decimal vacationBalance = _model.GetVacationBalanceInDays();
... // Formatam si returnam un string vream sa afieze 2 saptamani, 3 zile}
}
}
View:
<Label Text="{Binding FormattedVacationBalance}" />
Extinderea prin Viewmodel
• O clasa din model poate furniza uneori doar un ID pentru datele
relationate – este necesar sa utilizam mai multe clase din model
• Ex. Dorim sa afisam proiectele in care este implicat un angajat – clasa
CompanyProjects
In EmployeeViewModel expunem o proprietate publica:
public IEnumerable<string> ActiveProjects => CompanyProjects.All .Where(p =>
p.Owner == _model.Id && p.IsActive) .Select(p => p.Name);
Extinderea prin Viewmodel
public string Name
{
get => _model.Name;
set => _model.Name = value;
}

public string Name {


get {...}
set { _model.Name = value;
OnPropertyChanged(nameof(Name)) } }
...
public void OnPropertyChanged(string propertyName)
{ ... // raise event }
Utilizarea comenzilor in ViewModel
• Two-way binding pentru a reactiona la modificarile din
• Butoane, MenuItem – nu sunt legate direct de modificarea datelor-
necesita gestiunea de event-like handling
• Nu dorim sa fie gestionate ca si evenimente de tip Clicked sau
Selected in code-behind, ci dorim sa le gestionam in viewmodel
pentru a putea fi usor testabile
Utilizarea pattern-ului Command
• Multe controale Xamarin.Forms care presuspun interactiuni de tip event suporta binding catre o proprietate
Command
<Button Text="Give Bonus" Command="{Binding GiveBonus}">

public class EmployeeViewModel : INotifyPropertyChanged


{ public ICommand GiveBonus
{get; set;}
... }

public interface ICommand


{ bool CanExecute(object parameter);
void Execute(object parameter);
event EventHandler CanExecuteChanged;
}
Metoda Execute – se apeleaza cand se apasa click pe buton – ICommand.Execute inlocuieste codul pentru
event-handling
CanExecute si CanExecuteChanged – se utilizeaza pentru a determina daca un control va fi afisat
enabled/disabled
Pattern-ul architectural MVC
• Aplicatii usor de testat si
actualizat
• modele, controller,view-uri
multiple
• Delimitarea
responsabilitatilor –este
mai usor sa scriem cod,
debug, testam decat o
component care are
dependinte in 2/3
componente
MVC
• 3 componente- delimitarea responsabilitatilor->debug si testare facila,
modificari usor de gestionat (modificam doar logica de business nu si UI)
• Cererile utilizatorilor sunt routate catre un Controller, care este responsabil
de lucrul cu Modelul pentru a gestiona actiunile utilizatorului si/sau a
prelua rezultatul interogarilor
• Controllerul alege View-ul care va fi afisat la utilizator si furnizeaza datele
din Model necesare folosind informatiile de routare
• Controlere nu ar trebui sa fie extrem de complexe- scoatem logica de
business din controler in model
Pattern-ul architectural MVC
• Ajuta la crearea de aplicatii care separa diverse aspecte ale aplicatiei
(logica de input, logica de business si logica UI) oferind in acelasi timp o
cuplare redusa intre aceste elemente
• Patternul specifica unde trebuie sa fie localizata fiecare tip de logica :
• Logica UI apartine View-ului
• Logica de input apartine controllerului
• Logica de business apartine modelului

• Aceasta separare ajuta la gestiunea complexitatii aplicatiei pentru ca


permite lucrul la un aspect al implementarii la un anumit moment dat fara
a impacta codul in alta parte
Controlerele
• Se afla in centrul aplicatiilor ASP.NET MVC
• Fiecare controler este responsabil pentru gestiunea cererilor pe baza
unei routari
• Pot sa actualizeze datele intr-un model si sa selecteze un view pentru
a fi afisat utilizatorului
• Fiecare controler deriva din clasa de baza Controller care se afla in
namespace-ul Microsoft.AspNetCore.Mvc
• Fiecare controler returneaza un obiect IActionResult de la action
methods - view (aplicatiile web) sau un set de date (web API)
Controlerele
• MVC invoca clase controller si action methods pe care le contin in
functie de URL-ul pe care il primesc
• Formatul default al URL-ului :
/[Controller]/[ActionName]/[Parameters]
• Formatul este setat in metoda Configure method din
fisierul Startup.cs
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"); });
Modelele
• Logica de business a aplicatiei e incorporata in model
• Fiecare clasa din model poate reprezenta entitati
• Realizand binding-ul intre view si model, fiecare view va detecta ce
anume si in ce fel sa afiseze datele
View-uri
• Reprezinta layer-ul de UI
• Sunt client-side – HTML, CSS, JavaScript care vor fi afisate
utilizatorului
• Sunt fisiere .cshtml si pot include cod atat pe parte de server cat si
partea de client
View-uri
• Un view este un HTML Template care are incorporat markup Razor
• Controllerul Home are o corespondenta in Folderul Views-Home
• Folderul Home contine view-uri pentru paginile About, Contact,
si Index (homepage)
• Cand un utilizator face o cerere catre una din cele trei pagini ,
controller actions din Controllerul Home determina care dintre cele
trei view-uri vor fi folosite pentru a construi si returna o pagina la
utilizator
Routing
• ASP.NET Core MVC construit pe baza ASP.NET Core routing, o
componeta URL-mapping

• Controllerele utilizeaza routarea pentru a mapa URL-urile cererilor la


actiuni pe baza unor route template

• prin sintaxa predefinita


• pe baza de atribute
Rutarea prin sintaxa predefinita
• Permite definirea unui format URL global acceptat de aplicatie si
specifica cum formatul predefinit poate fi mapat la o un action metod
specfic sau un anumit controller
• Cand se primeste o cerere – routing engine parseaza URL-ul si il
asociaza unuia din formatele definite, iar apoi apeleaza action metod
asociat
routes.MapRoute(name: "Default", template:
"{controller=Home}/{action=Index}/{id?}");

/Products/Details/5
Rutarea prin sintaxa predefinita
• Extrage valorile { controller = Products, action = Details, id = 5 }
• Extragerea valorilor se mapeaza daca exista in aplicatie un Controller
care se numeste ProductsController si o actiune Details :

public class ProductsController : Controller


{ public IActionResult Details(int id)
{ return
ControllerContext.MyDisplayRouteInfo(id);
}
}
Routarea pe baza de atribute
• Permite specificarea informatiei referitoare la rutare prin atribute
specificate controlerelor
• Definitiile de rutare sunt specificate langa controlerul sau actiunea cu
care sunt asociate
[Route("api/[controller]")]
public class ProductsController : Controller
{ [HttpGet("{id}")]
public IActionResult GetProduct(int id)
{ ... }
}

S-ar putea să vă placă și